linkedin-v2 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|