linkedin-v2 0.1.3
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 +7 -0
- data/CHANGELOG.md +10 -0
- data/CONTRIBUTING.md +1 -0
- data/Gemfile +8 -0
- data/LICENSE +22 -0
- data/README.md +224 -0
- data/Rakefile +19 -0
- data/lib/linked_in/access_token.rb +24 -0
- data/lib/linked_in/api.rb +108 -0
- data/lib/linked_in/api_resource.rb +180 -0
- data/lib/linked_in/communications.rb +40 -0
- data/lib/linked_in/configuration.rb +41 -0
- data/lib/linked_in/connection.rb +35 -0
- data/lib/linked_in/errors.rb +73 -0
- data/lib/linked_in/jobs.rb +11 -0
- data/lib/linked_in/mash.rb +68 -0
- data/lib/linked_in/media.rb +13 -0
- data/lib/linked_in/oauth2.rb +223 -0
- data/lib/linked_in/organizations.rb +217 -0
- data/lib/linked_in/people.rb +151 -0
- data/lib/linked_in/raise_error.rb +28 -0
- data/lib/linked_in/search.rb +70 -0
- data/lib/linked_in/share_and_social_stream.rb +143 -0
- data/lib/linked_in/version.rb +3 -0
- data/lib/linkedin-v2.rb +52 -0
- data/linkedin-v2.gemspec +39 -0
- data/pkg/linkedin-oauth2-2.0.0.gem +0 -0
- data/spec/linked_in/api/api_spec.rb +41 -0
- data/spec/linked_in/api/communications_spec.rb +13 -0
- data/spec/linked_in/api/jobs_spec.rb +33 -0
- data/spec/linked_in/api/organizations_spec.rb +54 -0
- data/spec/linked_in/api/people_spec.rb +191 -0
- data/spec/linked_in/api/search_spec.rb +71 -0
- data/spec/linked_in/api/share_and_social_stream_spec.rb +87 -0
- data/spec/linked_in/configuration_spec.rb +46 -0
- data/spec/linked_in/connection_spec.rb +10 -0
- data/spec/linked_in/module_loading_spec.rb +23 -0
- data/spec/linked_in/oauth/access_token_spec.rb +27 -0
- data/spec/linked_in/oauth/auth_code_spec.rb +86 -0
- data/spec/linked_in/oauth/credentials_spec.rb +96 -0
- data/spec/linked_in/oauth/get_access_token_spec.rb +108 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/vcr_cassettes/access_token_success.yml +99 -0
- data/spec/vcr_cassettes/bad_code.yml +99 -0
- data/spec/vcr_cassettes/organization_data.yml +51 -0
- data/spec/vcr_cassettes/people_picture_urls.yml +52 -0
- data/spec/vcr_cassettes/people_profile_connections_fields.yml +52 -0
- data/spec/vcr_cassettes/people_profile_connections_other.yml +52 -0
- data/spec/vcr_cassettes/people_profile_connections_self.yml +52 -0
- data/spec/vcr_cassettes/people_profile_fields_complex.yml +52 -0
- data/spec/vcr_cassettes/people_profile_fields_simple.yml +52 -0
- data/spec/vcr_cassettes/people_profile_lang_spanish.yml +53 -0
- data/spec/vcr_cassettes/people_profile_multiple_fields.yml +52 -0
- data/spec/vcr_cassettes/people_profile_multiple_uids.yml +52 -0
- data/spec/vcr_cassettes/people_profile_multiple_uids_and_urls.yml +52 -0
- data/spec/vcr_cassettes/people_profile_multiple_urls.yml +52 -0
- data/spec/vcr_cassettes/people_profile_new_connections_fields.yml +52 -0
- data/spec/vcr_cassettes/people_profile_new_connections_other.yml +52 -0
- data/spec/vcr_cassettes/people_profile_new_connections_self.yml +52 -0
- data/spec/vcr_cassettes/people_profile_other_uid.yml +57 -0
- data/spec/vcr_cassettes/people_profile_other_url.yml +54 -0
- data/spec/vcr_cassettes/people_profile_own.yml +57 -0
- data/spec/vcr_cassettes/people_profile_own_secure.yml +53 -0
- data/spec/vcr_cassettes/people_profile_skills.yml +52 -0
- data/spec/vcr_cassettes/unavailable.yml +99 -0
- metadata +285 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 188d3b8c708aa23a58b655c5e2196919bb9bba99
|
4
|
+
data.tar.gz: b8391342b546d18f5b8c322a002a81d262863745
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ca09e965f66642dfc2dec5d63554be7980a828e6f0c4cfc6d2e20f8bc923209971013319131e0e2f10469e163c03fb958b3fa100d368252d15b431b232e2cc40
|
7
|
+
data.tar.gz: e2cc077b53c6e9bf080d73822e18fcad500e6a5a6a1d9f8abf71da3ade3d1d1082268fed3aabea01cf5bdb38f03b1629c75e15923e86ea61715415a009556140
|
data/CHANGELOG.md
ADDED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Read [GitHub's "Contributing to Open Source on GitHub"](https://guides.github.com/activities/contributing-to-open-source/)
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2014-present Evan Morikawa (evan@evanmorikawa.com) 2013-2014
|
4
|
+
Matt Kirk 2009-11 Wynn Netherland.
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
# LinkedIn v2
|
2
|
+
|
3
|
+
## WARNING: DANGER WILL ROBINSON
|
4
|
+
|
5
|
+
This is very much a work in progress. Currently only the shares/social stream
|
6
|
+
and organization related endpoints have been verified to work. Having said that,
|
7
|
+
those endpoints are using this gem in a production application, so they're
|
8
|
+
reliable enough. :)
|
9
|
+
|
10
|
+
Many endpoints still need to be written, and as I don't have the requisite partner
|
11
|
+
status w/ LinkedIn, I can't develop against the jobs API. Many of the specs still
|
12
|
+
fail. *Caveat emptor*
|
13
|
+
|
14
|
+
## NOW BACK TO YOUR REGULARLY SCHEDULED PROGRAMMING
|
15
|
+
|
16
|
+
Ruby wrapper for v2 if the [LinkedIn API](http://developer.linkedin.com). This gem is entirely
|
17
|
+
based on emorikawa's excellent [linkedin-oauth2](https://github.com/emorikawa/linkedin-oauth2)
|
18
|
+
gem.
|
19
|
+
|
20
|
+
If you are using OAuth 1.0, see [hexgnu/linkedin](https://github.com/hexgnu/linkedin)
|
21
|
+
If you are using OAuth 2.0 and the v1 LinkedIn API, see [emorikawa/linkedin-oauth2](https://github.com/emorikawa/linkedin-oauth2), on which this gem is based.
|
22
|
+
|
23
|
+
# Installation
|
24
|
+
|
25
|
+
In Bundler:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
gem "linkedin-v2", "~> 0.1.0"
|
29
|
+
```
|
30
|
+
|
31
|
+
Otherwise:
|
32
|
+
|
33
|
+
[sudo|rvm] gem install linkedin-v2
|
34
|
+
|
35
|
+
# Usage
|
36
|
+
|
37
|
+
**[Step 1:](#step-1-register-your-application)** [Register](https://www.linkedin.com/secure/developer) your
|
38
|
+
application with LinkedIn. They will give you a **Client ID** (aka API
|
39
|
+
Key) and a **Client Secret** (aka Secret Key)
|
40
|
+
|
41
|
+
**[Step 2:](#step-2-getting-an-access-token)** Use your **Client ID** and **Client Secret** to obtain an **Access Token** from some user.
|
42
|
+
|
43
|
+
**[Step 3:](#step-3-using-linkedins-api)** Use an **Access Token** to query the API.
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
api = LinkedIn::API.new(access_token)
|
47
|
+
me = api.profile
|
48
|
+
```
|
49
|
+
|
50
|
+
## Step 1: Register your Application
|
51
|
+
|
52
|
+
You first need to create and register an application with LinkedIn
|
53
|
+
[here](https://www.linkedin.com/secure/developer).
|
54
|
+
|
55
|
+
You will not be able to use any part of the API without registering first.
|
56
|
+
|
57
|
+
Once you have registered you will need to take note of a few key items on
|
58
|
+
your Application Details page.
|
59
|
+
|
60
|
+
1. **API Key** - We refer to this as your client id or `client_id`
|
61
|
+
1. **Secret Key** - We refer to this as your client secret or
|
62
|
+
`client_secret`
|
63
|
+
1. **Default Scope** - This is the set of permissions you request from
|
64
|
+
users when they connect to your app. If you want to set this on a
|
65
|
+
request-by-request basis, you can use the `scope` option with the
|
66
|
+
`auth_code_url` method.
|
67
|
+
1. **OAuth 2.0 Redirect URLs** - For security reasons, the url you enter
|
68
|
+
in this box must exactly match the `redirect_uri` you use in this gem.
|
69
|
+
|
70
|
+
You do NOT need **OAuth User Token** nor **OAuth User Secret**. That is
|
71
|
+
for OAuth 1.0. This gem is for OAuth 2.0.
|
72
|
+
|
73
|
+
## Step 2: Getting An Access Token
|
74
|
+
|
75
|
+
All LinkedIn API requests must be made in the context of an access token.
|
76
|
+
The access token encodes what LinkedIn information your AwesomeApp® can
|
77
|
+
gather on behalf of "John Doe".
|
78
|
+
|
79
|
+
There are a few different ways to get an access token from a user.
|
80
|
+
|
81
|
+
1. You can use [LinkedIn's Javascript API](https://developer.linkedin.com/documents/javascript-api-reference-0) to authenticate on the front-end and then pass the access token to the backend via [this procedure](https://developer.linkedin.com/documents/exchange-jsapi-tokens-rest-api-oauth-tokens).
|
82
|
+
|
83
|
+
1. If you use OmniAuth, I would recommend looking at [decioferreira/omniauth-linkedin-oauth2](https://github.com/decioferreira/omniauth-linkedin-oauth2) to help automate authentication.
|
84
|
+
|
85
|
+
1. You can do it manually using this linkedin-oauth2 gem and the steps
|
86
|
+
below.
|
87
|
+
|
88
|
+
Here is how to get an access token using this linkedin-oauth2 gem:
|
89
|
+
|
90
|
+
### Step 2A: Configuration
|
91
|
+
|
92
|
+
You will need to configure the following items:
|
93
|
+
|
94
|
+
1. Your **client id** (aka API Key)
|
95
|
+
1. Your **client secret** (aka Secret Key)
|
96
|
+
1. Your **redirect uri**. On LinkedIn's website you must input a list of
|
97
|
+
valid redirect URIs. If you use the same one each time, you can set it
|
98
|
+
in the `LinkedIn.configure` block. If your redirect uris change
|
99
|
+
depending on business logic, you can pass it into the `auth_code_url`
|
100
|
+
method.
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
# It's best practice to keep secret credentials out of source code.
|
104
|
+
# You can, of course, hardcode dev keys or directly pass them in as the
|
105
|
+
# first two arguments of LinkedIn::OAuth2.new
|
106
|
+
LinkedIn.configure do |config|
|
107
|
+
config.client_id = ENV["LINKEDIN_CLIENT_ID"]
|
108
|
+
config.client_secret = ENV["LINKEDIN_CLIENT_SECRET"]
|
109
|
+
|
110
|
+
# This must exactly match the redirect URI you set on your application's
|
111
|
+
# settings page. If your redirect_uri is dynamic, pass it into
|
112
|
+
# `auth_code_url` instead.
|
113
|
+
config.redirect_uri = "https://getawesomeapp.io/linkedin/oauth2"
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
### Step 2B: Get Auth Code URL
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
oauth = LinkedIn::OAuth2.new
|
121
|
+
|
122
|
+
url = oauth.auth_code_url
|
123
|
+
```
|
124
|
+
|
125
|
+
### Step 2C: User Sign In
|
126
|
+
|
127
|
+
You must now load url from Step 2B in a browser. It will pull up the
|
128
|
+
LinkedIn sign in box. Once LinkedIn user credentials are entered, the box
|
129
|
+
will close and redirect to your redirect url, passing along with it the
|
130
|
+
**OAuth code** as the `code` GET param.
|
131
|
+
|
132
|
+
Be sure to read the extended documentation around the LinkedIn::OAuth2
|
133
|
+
module for more options you can set.
|
134
|
+
|
135
|
+
**Note:** The **OAuth code** only lasts for ~20 seconds!
|
136
|
+
|
137
|
+
### Step 2D: Get Access Token
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
code = "THE_OAUTH_CODE_LINKEDIN_GAVE_ME"
|
141
|
+
|
142
|
+
access_token = oauth.get_access_token(code)
|
143
|
+
```
|
144
|
+
|
145
|
+
Now that you have an access token, you can use it to query the API.
|
146
|
+
|
147
|
+
The `LinkedIn::OAuth2` inherits from [intreda/oauth2](https://github.com/intridea/oauth2)'s `OAuth2::Client` class. See that gem's [documentation](https://github.com/intridea/oauth2/blob/master/lib/oauth2/client.rb) for more usage examples.
|
148
|
+
|
149
|
+
## Step 3: Using LinkedIn's API
|
150
|
+
|
151
|
+
Once you have an access token, you can query LinkedIn's API.
|
152
|
+
|
153
|
+
Your access token encodes the permissions you're allowed to have. See Step
|
154
|
+
2 and [this LinkedIn document](https://developer.linkedin.com/documents/authentication#granting) for how to change the permissions. See each section's documentation on LinkedIn for more information on what permissions get you access to.
|
155
|
+
|
156
|
+
### People
|
157
|
+
|
158
|
+
## TBD
|
159
|
+
|
160
|
+
### Organizations
|
161
|
+
|
162
|
+
Detailed overviews of Organizations
|
163
|
+
|
164
|
+
See https://developer.linkedin.com/docs/guide/v2/organizations
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
# Organization info
|
168
|
+
api.organization(name: "google")
|
169
|
+
api.organization(id: 12345)
|
170
|
+
api.organization(urn: 'urn:li:organization:12345')
|
171
|
+
```
|
172
|
+
|
173
|
+
### Jobs
|
174
|
+
|
175
|
+
## DON'T HAVE ACCESS. :(
|
176
|
+
```
|
177
|
+
|
178
|
+
### Share and Social Stream
|
179
|
+
|
180
|
+
View and update content on social streams
|
181
|
+
|
182
|
+
See https://developer.linkedin.com/docs/guide/v2/shares
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
# Your news feed
|
186
|
+
api.shares
|
187
|
+
|
188
|
+
api.share(content: "hi")
|
189
|
+
|
190
|
+
# For a particular feed item
|
191
|
+
api.comments(urn: "urn:li:article:12345")
|
192
|
+
api.likes(urn: "urn:li:article:12345")
|
193
|
+
|
194
|
+
api.like(urn: "urn:li:activity:12345")
|
195
|
+
api.unlike(urn: "urn:li:activity:12345")
|
196
|
+
```
|
197
|
+
|
198
|
+
### Communications
|
199
|
+
|
200
|
+
## TBD
|
201
|
+
|
202
|
+
# Documentation
|
203
|
+
|
204
|
+
On [RubyDoc here](http://rubydoc.info/github/mdesjardins/linkedin-v2/frames/file/README.md)
|
205
|
+
|
206
|
+
Read the source for [LinkedIn::API](https://github.com/mdesjardins/linkedin-v2/blob/master/lib/linked_in/api.rb) and [LinkedIn::OAuth2](https://github.com/mdesjardins/linkedin-v2/blob/master/lib/linked_in/oauth2.rb)
|
207
|
+
|
208
|
+
# Contributing
|
209
|
+
|
210
|
+
Please see [CONTRIBUTING.md](https://github.com/mdesjardins/linkedin-v2/blob/master/CONTRIBUTING.md) for details.
|
211
|
+
|
212
|
+
# Credits
|
213
|
+
Huge, huge props to Evan Morikawa for writing the v1 version of this gem. This gem is
|
214
|
+
pretty much all of that work, but gutted and replaced with v2 endpoints.
|
215
|
+
|
216
|
+
* [Evan Morikawa](https://twitter.com/eom) ([emorikawa](https://github.com/emorikawa))
|
217
|
+
* [Matt Kirk](http://matthewkirk.com) ([hexgnu](https://github.com/hexgnu))
|
218
|
+
* [Wynn Netherland](http://wynnetherland.com) ([pengwynn](https://github.com/pengwynn))
|
219
|
+
* Josh Kalderimis ([joshk](https://github.com/joshk))
|
220
|
+
* Erik Michaels-Ober ([sferik](https://github.com/sferik))
|
221
|
+
* And Many More [Contributors](https://github.com/emorikawa/linkedin-oauth2/graphs/contributors)
|
222
|
+
|
223
|
+
# License
|
224
|
+
Copyright :copyright: 2018-present [Mike Desjardins](https://twitter.com/mdesjardins) 2014-2018 [Evan Morikawa](https://twitter.com/e0m) 2013-2014 [Matt Kirk](http://matthewkirk.com/) 2009-11 [Wynn Netherland](http://wynnnetherland.com/) and [contributors](https://github.com/emorikawa/linkedin-oauth2/graphs/contributors). It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. See [LICENSE](https://github.com/emorikawa/linkedin-oauth2/blob/master/LICENSE) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Adds the following task:
|
2
|
+
# spec - Run RSpec tests & setup $LOAD_PATH properly
|
3
|
+
#
|
4
|
+
# We recommend you set the following RSpec options in your own ~/.rspec
|
5
|
+
# --color
|
6
|
+
# --format documentation
|
7
|
+
# --profile
|
8
|
+
# --order rand
|
9
|
+
require 'rspec/core/rake_task'
|
10
|
+
RSpec::Core::RakeTask.new(:spec)
|
11
|
+
|
12
|
+
# Adds the following tasks:
|
13
|
+
# build - Build gem in pkg/ directory
|
14
|
+
# install - Build and install gem
|
15
|
+
# release - Create tag, build gem, and push it to Rubygems
|
16
|
+
require 'bundler/gem_helper'
|
17
|
+
Bundler::GemHelper.install_tasks
|
18
|
+
|
19
|
+
task default: :spec
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
# A simple data object to contain the token string and expiration data.
|
3
|
+
class AccessToken
|
4
|
+
attr_accessor :token, :expires_in, :expires_at
|
5
|
+
|
6
|
+
# Creates a simple data wrapper for an access token.
|
7
|
+
#
|
8
|
+
# LinkedIn returns only an `expires_in` value. This calculates and
|
9
|
+
# sets and `expires_at` field for convenience.
|
10
|
+
#
|
11
|
+
# @param [String] token the access token
|
12
|
+
# @param [FixNum] expires_in number of seconds the token lasts for
|
13
|
+
# @param [Time] expires_at when the token will expire.
|
14
|
+
def initialize(token=nil, expires_in=nil, expires_at=nil)
|
15
|
+
self.token = token
|
16
|
+
self.expires_in = expires_in
|
17
|
+
if expires_at.nil? and not self.expires_in.nil?
|
18
|
+
self.expires_at = Time.now + expires_in
|
19
|
+
else
|
20
|
+
self.expires_at = expires_at
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
class API
|
3
|
+
|
4
|
+
attr_accessor :access_token
|
5
|
+
|
6
|
+
def initialize(access_token=nil)
|
7
|
+
access_token = parse_access_token(access_token)
|
8
|
+
verify_access_token!(access_token)
|
9
|
+
@access_token = access_token
|
10
|
+
|
11
|
+
@connection = LinkedIn::Connection.new params: default_params,
|
12
|
+
headers: default_headers
|
13
|
+
|
14
|
+
initialize_endpoints
|
15
|
+
end
|
16
|
+
|
17
|
+
extend Forwardable # Composition over inheritance
|
18
|
+
|
19
|
+
# I do not have access to the jobs related endpoints.
|
20
|
+
# def_delegators :@jobs, :job,
|
21
|
+
# :job_bookmarks,
|
22
|
+
# :job_suggestions,
|
23
|
+
# :add_job_bookmark
|
24
|
+
|
25
|
+
def_delegators :@people, :profile,
|
26
|
+
:skills,
|
27
|
+
:connections,
|
28
|
+
:picture_urls,
|
29
|
+
:new_connections
|
30
|
+
|
31
|
+
def_delegators :@search, :search
|
32
|
+
|
33
|
+
# Not part of v2??
|
34
|
+
# def_delegators :@groups, :join_group,
|
35
|
+
# :group_posts,
|
36
|
+
# :group_profile,
|
37
|
+
# :add_group_share,
|
38
|
+
# :group_suggestions,
|
39
|
+
# :group_memberships,
|
40
|
+
# :post_group_discussion
|
41
|
+
|
42
|
+
def_delegators :@organizations, :organization,
|
43
|
+
:brand,
|
44
|
+
:organization_acls,
|
45
|
+
:organization_search,
|
46
|
+
:organization_page_statistics,
|
47
|
+
:organization_follower_statistics,
|
48
|
+
:organization_share_statistics
|
49
|
+
|
50
|
+
def_delegators :@communications, :send_message
|
51
|
+
|
52
|
+
def_delegators :@share_and_social_stream, :shares,
|
53
|
+
:share,
|
54
|
+
:likes,
|
55
|
+
:like,
|
56
|
+
:unlike,
|
57
|
+
:comments,
|
58
|
+
:comment
|
59
|
+
# :update_comment,
|
60
|
+
# :network_updates
|
61
|
+
|
62
|
+
def_delegators :@media, :summary
|
63
|
+
|
64
|
+
private ##############################################################
|
65
|
+
|
66
|
+
def initialize_endpoints
|
67
|
+
@jobs = LinkedIn::Jobs.new(@connection)
|
68
|
+
@people = LinkedIn::People.new(@connection)
|
69
|
+
@search = LinkedIn::Search.new(@connection)
|
70
|
+
# @groups = LinkedIn::Groups.new(@connection) not supported by v2 API?
|
71
|
+
@organizations = LinkedIn::Organizations.new(@connection)
|
72
|
+
@communications = LinkedIn::Communications.new(@connection)
|
73
|
+
@share_and_social_stream = LinkedIn::ShareAndSocialStream.new(@connection)
|
74
|
+
@media = LinkedIn::Media.new(@connection)
|
75
|
+
end
|
76
|
+
|
77
|
+
def default_params
|
78
|
+
# LIv2 TODO - Probably can just remove?
|
79
|
+
# https//developer.linkedin.com/documents/authentication
|
80
|
+
#return { oauth2_access_token: @access_token.token }
|
81
|
+
{}
|
82
|
+
end
|
83
|
+
|
84
|
+
def default_headers
|
85
|
+
# https://developer.linkedin.com/documents/api-requests-json
|
86
|
+
return {"x-li-format" => "json", "Authorization" => "Bearer #{@access_token.token}"}
|
87
|
+
end
|
88
|
+
|
89
|
+
def verify_access_token!(access_token)
|
90
|
+
if not access_token.is_a? LinkedIn::AccessToken
|
91
|
+
raise no_access_token_error
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def parse_access_token(access_token)
|
96
|
+
if access_token.is_a? LinkedIn::AccessToken
|
97
|
+
return access_token
|
98
|
+
elsif access_token.is_a? String
|
99
|
+
return LinkedIn::AccessToken.new(access_token)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def no_access_token_error
|
104
|
+
msg = LinkedIn::ErrorMessages.no_access_token
|
105
|
+
LinkedIn::InvalidRequest.new(msg)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
module LinkedIn
|
2
|
+
# The abstract class all API endpoints inherit from. Providers common
|
3
|
+
# builder methods across all endpoints.
|
4
|
+
#
|
5
|
+
# @!macro profile_options
|
6
|
+
# @options opts [String] :id LinkedIn ID to fetch profile for
|
7
|
+
# @options opts [String] :url The profile url
|
8
|
+
# @options opts [String] :lang Requests the language of the profile.
|
9
|
+
# Options are: en, fr, de, it, pt, es
|
10
|
+
# @options opts [Array, Hash] :fields fields to fetch. The list of
|
11
|
+
# fields can be found at
|
12
|
+
# https://developer.linkedin.com/documents/profile-fields
|
13
|
+
# @options opts [String] :secure (true) specify if urls in the
|
14
|
+
# response should be https
|
15
|
+
# @options opts [String] :"secure-urls" (true) alias to secure option
|
16
|
+
#
|
17
|
+
# @!macro share_input_fields
|
18
|
+
# @param [Hash] share content of the share
|
19
|
+
# @option share [String] :comment
|
20
|
+
# @option share [String] :content
|
21
|
+
# @option share [String] :title
|
22
|
+
# @option share [String] :submitted-url
|
23
|
+
# @option share [String] :submitted-image-url
|
24
|
+
# @option share [String] :description
|
25
|
+
# @option share [String] :visibility
|
26
|
+
# @option share [String] :code
|
27
|
+
#
|
28
|
+
# @!macro organization_path_options
|
29
|
+
# @param [Hash] options identifies the organization profile you want
|
30
|
+
# @option options [String] :email_domain organization email domain
|
31
|
+
# @option options [String] :id organization ID
|
32
|
+
# @option options [String] :urn organization URN
|
33
|
+
# @option options [String] :vanity_name organization vanity name
|
34
|
+
#
|
35
|
+
# @!macro brand_path_options
|
36
|
+
# @param [Hash] options identifies the brand profile you want
|
37
|
+
# @option options [String] :id brand ID
|
38
|
+
# @option options [String] :vanity_name brand vanity name
|
39
|
+
# @option options [String] :parent_id brand's parent organization ID.
|
40
|
+
class APIResource
|
41
|
+
|
42
|
+
def initialize(connection)
|
43
|
+
@connection = connection
|
44
|
+
end
|
45
|
+
|
46
|
+
def urn_to_id(urn)
|
47
|
+
urn.split(':').last
|
48
|
+
end
|
49
|
+
|
50
|
+
def id_to_urn(resource, id)
|
51
|
+
['urn', 'li', resource, id].join(':')
|
52
|
+
end
|
53
|
+
|
54
|
+
protected ############################################################
|
55
|
+
|
56
|
+
def get(path, options={})
|
57
|
+
url, params, headers = prepare_connection_params(path, options)
|
58
|
+
|
59
|
+
response = @connection.get(url, params, headers)
|
60
|
+
Mash.from_json(response.body)
|
61
|
+
end
|
62
|
+
|
63
|
+
def post(path=nil, body=nil, headers=nil, &block)
|
64
|
+
@connection.post(prepend_prefix(path), body, headers, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
def put(path=nil, body=nil, headers=nil, &block)
|
68
|
+
@connection.put(prepend_prefix(path), body, headers, &block)
|
69
|
+
Mash.from_json(response.body)
|
70
|
+
end
|
71
|
+
|
72
|
+
def delete(path=nil, body=nil, headers=nil, &block)
|
73
|
+
# @connection.delete(prepend_prefix(path), params, headers, &block)
|
74
|
+
# To be able to DELETE with a body:
|
75
|
+
reponse = @connection.run_request(:delete, prepend_prefix(path), body, headers, &block)
|
76
|
+
|
77
|
+
Mash.from_json(response.body)
|
78
|
+
end
|
79
|
+
|
80
|
+
def deprecated
|
81
|
+
LinkedIn::Deprecated.new(LinkedIn::ErrorMessages.deprecated)
|
82
|
+
end
|
83
|
+
|
84
|
+
private ##############################################################
|
85
|
+
|
86
|
+
def prepend_prefix(path)
|
87
|
+
return @connection.path_prefix + path
|
88
|
+
end
|
89
|
+
|
90
|
+
def prepare_connection_params(path, options)
|
91
|
+
path = prepend_prefix(path)
|
92
|
+
path += generate_field_selectors(options)
|
93
|
+
|
94
|
+
headers = options.delete(:headers) || {}
|
95
|
+
|
96
|
+
params = format_options_for_query(options)
|
97
|
+
|
98
|
+
return [path, params, headers]
|
99
|
+
end
|
100
|
+
|
101
|
+
# Dasherizes the param keys
|
102
|
+
def format_options_for_query(options)
|
103
|
+
options.reduce({}) do |list, kv|
|
104
|
+
key, value = kv.first.to_s.gsub("_","-"), kv.last
|
105
|
+
list[key] = value
|
106
|
+
list
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def generate_field_selectors(options)
|
111
|
+
default = LinkedIn.config.default_profile_fields || {}
|
112
|
+
fields = options.delete(:fields) || default
|
113
|
+
if options.delete(:public)
|
114
|
+
return ":public"
|
115
|
+
elsif fields.empty?
|
116
|
+
return ""
|
117
|
+
else
|
118
|
+
return "?projection=(#{fields})"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def build_fields_params(fields)
|
123
|
+
if fields.is_a?(Hash) && !fields.empty?
|
124
|
+
fields.map {|v| "(#{build_fields_params(v)})" }.join(',')
|
125
|
+
elsif fields.respond_to?(:each)
|
126
|
+
fields.map {|field| build_fields_params(field) }.join(',')
|
127
|
+
else
|
128
|
+
fields.to_s.gsub("_", "-")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def profile_path(options={}, allow_multiple=true)
|
133
|
+
path = "/people"
|
134
|
+
|
135
|
+
id = options.delete(:id)
|
136
|
+
url = options.delete(:url)
|
137
|
+
|
138
|
+
ids = options.delete(:ids)
|
139
|
+
urls = options.delete(:urls)
|
140
|
+
|
141
|
+
if options.delete(:email) then raise deprecated end
|
142
|
+
|
143
|
+
if (id or url)
|
144
|
+
path += single_person_path(id, url)
|
145
|
+
elsif allow_multiple and (ids or urls)
|
146
|
+
path += multiple_people_path(ids, urls)
|
147
|
+
else
|
148
|
+
path = "/me"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def single_person_path(id=nil, url=nil)
|
153
|
+
if id
|
154
|
+
return "/id=#{id}"
|
155
|
+
elsif url
|
156
|
+
return "/url=#{CGI.escape(url)}"
|
157
|
+
else
|
158
|
+
return "/me"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# See syntax here: https://developer.linkedin.com/documents/field-selectors
|
163
|
+
def multiple_people_path(ids=[], urls=[])
|
164
|
+
if ids.nil? then ids = [] end
|
165
|
+
if urls.nil? then urls = [] end
|
166
|
+
|
167
|
+
ids = ids.map do |id|
|
168
|
+
if is_self(id) then "me" else "id=#{id}" end
|
169
|
+
end
|
170
|
+
urls = urls.map do |url|
|
171
|
+
if is_self(url) then "me" else "url=#{CGI.escape(url)}" end
|
172
|
+
end
|
173
|
+
return "::(#{(ids+urls).join(",")})"
|
174
|
+
end
|
175
|
+
|
176
|
+
def is_self(str)
|
177
|
+
str == "self" or str == "me"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|