Merge branch 'develop' into bug/642873
Conflicts: linkdrop/controllers/contacts.py linkdrop/tests/services/test_playback.py test.ini
This commit is contained in:
Коммит
f593a315a0
|
@ -86,9 +86,7 @@ Name of the group to return.
|
|||
],
|
||||
response={'type': 'object', 'doc': 'Portable Contacts Collection'})
|
||||
def get(self, domain):
|
||||
group = request.POST.get('group', None)
|
||||
startIndex = int(request.POST.get('startindex', '0'))
|
||||
maxResults = int(request.POST.get('maxresults', '25'))
|
||||
page_data = request.POST.get('pageData', None)
|
||||
account_data = request.POST.get('account', None)
|
||||
|
||||
acct = None
|
||||
|
@ -103,10 +101,10 @@ Name of the group to return.
|
|||
}
|
||||
return {'result': None, 'error': error}
|
||||
|
||||
page_data = page_data and json.loads(page_data) or {}
|
||||
try:
|
||||
services = get_services()
|
||||
result, error = services.getcontacts(domain, acct, startIndex,
|
||||
maxResults, group)
|
||||
result, error = services.getcontacts(domain, acct, page_data)
|
||||
except DomainNotRegisteredError:
|
||||
error = {
|
||||
'message': "'domain' is invalid",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"status": 302
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"protocol": "http",
|
||||
"reason": "automatic success save",
|
||||
"uri": "https://api.linkedin.com/uas/oauth/requestToken"
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
GET /uas/oauth/requestToken
|
||||
accept-encoding: gzip, deflate
|
||||
authorization: OAuth realm="", oauth_body_hash="2jmj7l5rSw0yVb%2FvlWAYkK%2FYBwk%3D", oauth_nonce="9999999", oauth_timestamp="1303186687", oauth_consumer_key="the_key", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_signature="the_sig%3D", oauth_callback="http%3A%2F%2Flinkdrop.caraveo.com%3A5000%2Fapi%2Faccount%2Fverify%3Fprovider%3Dlinkedin.com"
|
||||
user-agent: Python-httplib2/$Rev$
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
HTTP/1.1 200 OK
|
||||
status: 200
|
||||
content-location: https://api.linkedin.com/uas/oauth/requestToken
|
||||
vary: Accept-Encoding
|
||||
server: Apache-Coyote/1.1
|
||||
-content-encoding: gzip
|
||||
date: Tue, 19 Apr 2011 04:19:01 GMT
|
||||
content-type: text/plain
|
||||
|
||||
oauth_token=the_token&oauth_token_secret=the_secret&oauth_callback_confirmed=true&xoauth_request_auth_url=https%3A%2F%2Fapi.linkedin.com%2Fuas%2Foauth%2Fauthorize&oauth_expires_in=599
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"error": null,
|
||||
"result": {
|
||||
"entry": [
|
||||
{
|
||||
"accounts": [
|
||||
{
|
||||
"domain": "linkedin.com",
|
||||
"userid": "11111111",
|
||||
"username": ""
|
||||
}
|
||||
],
|
||||
"displayName": "Jean Reilly",
|
||||
"urls": [
|
||||
{
|
||||
"primary": true,
|
||||
"type": "profile",
|
||||
"value": "http://www.linkedin.com/profile?viewProfile=&key=999999&authToken=xxxx&authType=name&trk=api*xxx*xxx*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"protocol": "http",
|
||||
"reason": "automatic success save",
|
||||
"uri": "http://api.linkedin.com/v1/people/~/connections?count=25"
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
GET /v1/people/~/connections?count=25
|
||||
x-li-format: json
|
||||
accept-encoding: gzip, deflate
|
||||
user-agent: Python-httplib2/$Rev$
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
HTTP/1.1 200 OK
|
||||
status: 200
|
||||
content-location: http://api.linkedin.com/v1/people/~/connections?count=500&oauth_body_hash=K%2BiMpCQsduglOsYkdIUQZQMtaDM%3D&oauth_nonce999999&oauth_timestamp=1303186697&oauth_consumer_key=the_key&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=the_token&oauth_signature=the_sig%3D
|
||||
vary: x-li-format,Accept-Encoding
|
||||
server: Apache-Coyote/1.1
|
||||
-content-encoding: gzip
|
||||
date: Tue, 19 Apr 2011 04:19:09 GMT
|
||||
x-li-format: json
|
||||
content-type: application/json;charset=UTF-8
|
||||
|
||||
{
|
||||
"values": [{
|
||||
"headline": "Banking Professional",
|
||||
"id": "11111111",
|
||||
"lastName": "Reilly",
|
||||
"location": {
|
||||
"name": "Vancouver, Canada Area",
|
||||
"country": {"code": "ca"}
|
||||
},
|
||||
"siteStandardProfileRequest": {"url": "http://www.linkedin.com/profile?viewProfile=&key=999999&authToken=xxxx&authType=name&trk=api*xxx*xxx*"},
|
||||
"apiStandardProfileRequest": {
|
||||
"headers": {
|
||||
"values": [{
|
||||
"name": "x-li-auth-token",
|
||||
"value": "name:TVgH"
|
||||
}],
|
||||
"_total": 1
|
||||
},
|
||||
"url": "http://api.linkedin.com/v1/people/11111111"
|
||||
},
|
||||
"industry": "Banking",
|
||||
"firstName": "Jean"
|
||||
}],
|
||||
"_total": 1
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"error": null,
|
||||
"result": {
|
||||
"date": "Tue, 19 Apr 2011 05:24:44 GMT",
|
||||
"from": null,
|
||||
"location": "http://api.linkedin.com/v1/people/ppppppp/shares/sssssss",
|
||||
"server": "Apache-Coyote/1.1",
|
||||
"shorturl": null,
|
||||
"status": "201",
|
||||
"to": "anyone",
|
||||
"vary": "Accept-Encoding"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{"domain": "linkedin.com",
|
||||
"shareType": "public",
|
||||
"to": "anyone",
|
||||
"message": "nerdy for anyone",
|
||||
"link": "http://slashdot.org/",
|
||||
"title": "Slashdot: News for nerds, stuff that matters",
|
||||
"account": {"oauth_token": "foo", "oauth_token_secret": "bar", "username": "mylinkedinid"}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"protocol": "http",
|
||||
"reason": "automatic success save",
|
||||
"uri": "http://api.linkedin.com/v1/people/~/shares"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
POST /v1/people/~/shares
|
||||
x-li-format: json
|
||||
content-type: application/json
|
||||
authorization: OAuth realm="http://api.linkedin.com", oauth_body_hash="the_hash_value", oauth_nonce="111111", oauth_timestamp="1303190627", oauth_consumer_key="the_consumer_key", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_token="the_token", oauth_signature="the_sig%3D"
|
||||
user-agent: Python-httplib2/$Rev$
|
||||
|
||||
{"comment": "nerdy for anyone", "content": {"submitted-image-url": "", "description": "", "submitted-url": "http://slashdot.org/", "title": ""}, "visibility": {"code": "anyone"}}
|
|
@ -0,0 +1,7 @@
|
|||
HTTP/1.1 201 Created
|
||||
status: 201
|
||||
vary: Accept-Encoding
|
||||
server: Apache-Coyote/1.1
|
||||
location: http://api.linkedin.com/v1/people/ppppppp/shares/sssssss
|
||||
date: Tue, 19 Apr 2011 05:24:44 GMT
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"error": null,
|
||||
"result": {
|
||||
"content-length": "0",
|
||||
"date": "Tue, 19 Apr 2011 07:11:40 GMT",
|
||||
"from": null,
|
||||
"location": "http://api.linkedin.com/v1/people/pppppppp/shares/ssssssss",
|
||||
"server": "Apache-Coyote/1.1",
|
||||
"shorturl": null,
|
||||
"status": "201",
|
||||
"to": "connections-only",
|
||||
"vary": "Accept-Encoding"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{"domain": "linkedin.com",
|
||||
"shareType": "myConnections",
|
||||
"to": "connections-only",
|
||||
"message": "nerdy for my connections!",
|
||||
"link": "http://slashdot.org/",
|
||||
"title": "Slashdot: News for nerds, stuff that matters",
|
||||
"account": {"oauth_token": "foo", "oauth_token_secret": "bar", "username": "mylinkedinid"}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"protocol": "http",
|
||||
"reason": "automatic success save",
|
||||
"uri": "http://api.linkedin.com/v1/people/~/shares"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
POST /v1/people/~/shares
|
||||
x-li-format: json
|
||||
content-type: application/json
|
||||
authorization: OAuth realm="http://api.linkedin.com", oauth_body_hash="the_hash%3D", oauth_nonce="9999999", oauth_timestamp="1303197039", oauth_consumer_key="the_key", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_token="the_token", oauth_signature="the_sig%3D"
|
||||
user-agent: Python-httplib2/$Rev$
|
||||
|
||||
{"comment": "nerdy for my connections!", "content": {"submitted-image-url": "", "description": "", "submitted-url": "http://slashdot.org/", "title": ""}, "visibility": {"code": "connections-only"}}
|
|
@ -0,0 +1,8 @@
|
|||
HTTP/1.1 201 Created
|
||||
status: 201
|
||||
content-length: 0
|
||||
vary: Accept-Encoding
|
||||
server: Apache-Coyote/1.1
|
||||
location: http://api.linkedin.com/v1/people/pppppppp/shares/ssssssss
|
||||
date: Tue, 19 Apr 2011 07:11:40 GMT
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"error": null,
|
||||
"result": {
|
||||
"content-length": "0",
|
||||
"date": "Tue, 19 Apr 2011 07:14:32 GMT",
|
||||
"from": null,
|
||||
"server": "Apache-Coyote/1.1",
|
||||
"shorturl": null,
|
||||
"status": "201",
|
||||
"to": "ppppp",
|
||||
"vary": "Accept-Encoding"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{"domain": "linkedin.com",
|
||||
"shareType": "contact",
|
||||
"to": "ppppp",
|
||||
"message": "nerdy just for you!",
|
||||
"link": "http://slashdot.org/",
|
||||
"title": "Slashdot: News for nerds, stuff that matters",
|
||||
"account": {"oauth_token": "foo",
|
||||
"oauth_token_secret": "bar",
|
||||
"username": "mylinkedinid",
|
||||
"verifiedEmail": "me@somewhere.com"}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"protocol": "http",
|
||||
"reason": "automatic success save",
|
||||
"uri": "http://api.linkedin.com/v1/people/~/mailbox"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
POST /v1/people/~/mailbox
|
||||
x-li-format: json
|
||||
content-type: application/json
|
||||
authorization: OAuth realm="http://api.linkedin.com", oauth_body_hash="the_hash%3D", oauth_nonce="999999", oauth_timestamp="1303197211", oauth_consumer_key="the_key", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_token="the_token", oauth_signature="the_sig%3D"
|
||||
user-agent: Python-httplib2/$Rev$
|
||||
|
||||
{"body": "nerdy just for you!\n\n*Slashdot: News for nerds, stuff that matters*\n\nhttp://slashdot.org/\n\n\n\n--\nshared via Mozilla F1 for Firefox -- http://f1.mozillamessaging.com/\n:: share links with the people that matter to you ::\n", "recipients": {"values": [{"person": {"_path": "/people/ppppp"}}]}, "subject": "A web link has been shared with you"}
|
|
@ -0,0 +1,7 @@
|
|||
HTTP/1.1 201 Created
|
||||
date: Tue, 19 Apr 2011 07:14:32 GMT
|
||||
status: 201
|
||||
content-length: 0
|
||||
vary: Accept-Encoding
|
||||
server: Apache-Coyote/1.1
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
GET /1/statuses/followers.json?screen_name=mytwitterid
|
||||
GET /1/statuses/followers.json?screen_name=mytwitterid&cursor=-1
|
||||
accept-encoding: gzip, deflate
|
||||
user-agent: Python-httplib2/$Rev$
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ x-ratelimit-limit: 350
|
|||
content-type: application/json; charset=utf-8
|
||||
x-ratelimit-reset: 1302676561
|
||||
|
||||
[
|
||||
{
|
||||
"users": [
|
||||
{
|
||||
"follow_request_sent":false,
|
||||
"profile_background_image_url":"http:\/\/a3.twimg.com\/a\/1302541726\/images\/themes\/theme1\/bg.png",
|
||||
|
@ -99,4 +100,9 @@ x-ratelimit-reset: 1302676561
|
|||
"utc_offset":36000,
|
||||
"profile_background_color":"C0DEED"
|
||||
}
|
||||
]
|
||||
],
|
||||
"next_cursor":0,
|
||||
"previous_cursor":0,
|
||||
"next_cursor_str":"0",
|
||||
"previous_cursor_str":"0"
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
"result": {
|
||||
"entry": [],
|
||||
"itemsPerPage": 0,
|
||||
"startIndex": 0,
|
||||
"totalResults": 0
|
||||
"startIndex": 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
],
|
||||
"nickname": "test"
|
||||
}
|
||||
],
|
||||
"itemsPerPage": 1,
|
||||
"startIndex": 0,
|
||||
"totalResults": 1
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,8 +53,16 @@ def assert_messages_equal(got, expected):
|
|||
got_items = dict(parse_qsl(gotpl))
|
||||
expected_items = dict(parse_qsl(expectedpl))
|
||||
assert_dicts_with_oauth_equal(got_items, expected_items)
|
||||
elif got.get_content_type() == "application/json":
|
||||
got_items = json.loads(gotpl)
|
||||
expected_items = json.loads(expectedpl)
|
||||
assert_dicts_equal(got_items, expected_items)
|
||||
elif gotpl is None:
|
||||
assert expectedpl is None
|
||||
else:
|
||||
eq_(gotpl, expectedpl)
|
||||
# rstrip the payloads as some of our corpus items have trailing
|
||||
# newlines to stop git/hg/etc complaining...
|
||||
eq_(gotpl.rstrip(), expectedpl.rstrip())
|
||||
|
||||
|
||||
# Somewhat analogous to a protocap.ProtocolCapturingBase object - but
|
||||
|
@ -97,7 +105,7 @@ class HttpReplayer(ProtocolReplayer):
|
|||
gotheadersstr = "\r\n".join(
|
||||
["%s: %s" % (n, v.encode("ascii"))
|
||||
for n, v in headers.iteritems()])
|
||||
bodystr = gotheadersstr + "\r\n" + (body or '')
|
||||
bodystr = gotheadersstr + "\r\n\r\n" + (body or '')
|
||||
gotob = email.message_from_string(bodystr)
|
||||
if headers:
|
||||
if 'content-type' in gotob:
|
||||
|
@ -367,6 +375,28 @@ class GoogleReplayTestCase(ServiceReplayTestCase):
|
|||
raise AssertionError(req_type)
|
||||
|
||||
|
||||
class LinkedinReplayTestCase(ServiceReplayTestCase):
|
||||
def getDefaultRequest(self, req_type):
|
||||
# No 'send' - all send tests have an f1-request.json file...
|
||||
if req_type == "contacts":
|
||||
account = {"oauth_token": "foo", "oauth_token_secret": "bar",
|
||||
"profile": {"emails": [{'value': 'me@example.com'}],
|
||||
"displayName": "Me",
|
||||
},
|
||||
}
|
||||
return {'username': 'me',
|
||||
'userid': '123',
|
||||
'keys': "1,2,3",
|
||||
'account': json.dumps(account),
|
||||
'domain': 'linkedin.com',
|
||||
'maxresults': 500,
|
||||
}
|
||||
if req_type == "auth":
|
||||
return {'domain': 'linkedin.com', 'username': 'foo',
|
||||
'userid': 'bar'}
|
||||
raise AssertionError(req_type)
|
||||
|
||||
|
||||
def setupReplayers():
|
||||
from linkoauth.backends import facebook_
|
||||
facebook_.HttpRequestor = HttpReplayer
|
||||
|
@ -377,6 +407,8 @@ def setupReplayers():
|
|||
google_.OAuth2Requestor = HttpReplayer
|
||||
from linkoauth.backends import twitter_
|
||||
twitter_.OAuth2Requestor = HttpReplayer
|
||||
from linkoauth.backends import linkedin_
|
||||
linkedin_.OAuth2Requestor = HttpReplayer
|
||||
import linkoauth.oauth
|
||||
linkoauth.oauth.HttpRequestor = HttpReplayer
|
||||
HttpReplayer.to_playback = []
|
||||
|
@ -396,6 +428,8 @@ def teardownReplayers():
|
|||
google_.OAuth2Requestor = linkoauth.protocap.OAuth2Requestor
|
||||
from linkoauth.backends import twitter_
|
||||
twitter_.OAuth2Requestor = linkoauth.protocap.OAuth2Requestor
|
||||
from linkoauth.backends import linkedin_
|
||||
linkedin_.OAuth2Requestor = linkoauth.protocap.OAuth2Requestor
|
||||
from linkoauth import oauth
|
||||
oauth.HttpRequestor = linkoauth.protocap.HttpRequestor
|
||||
|
||||
|
@ -408,6 +442,7 @@ host_to_test = {
|
|||
'social.yahooapis.com': YahooReplayTestCase,
|
||||
'api.twitter.com': TwitterReplayTestCase,
|
||||
'twitter.com': TwitterReplayTestCase,
|
||||
'api.linkedin.com': LinkedinReplayTestCase,
|
||||
}
|
||||
|
||||
|
||||
|
|
6
test.ini
6
test.ini
|
@ -70,6 +70,12 @@ oauth.yahoo.com.app_id = FILL_ME_IN
|
|||
# set to true if you have completed domain verification with Yahoo
|
||||
oauth.yahoo.com.verified = 0
|
||||
|
||||
oauth.linkedin.com.consumer_key = A_BIT_OF_A_SECRET
|
||||
oauth.linkedin.com.consumer_secret = TOP_SECRET
|
||||
oauth.linkedin.com.request = https://api.linkedin.com/uas/oauth/requestToken
|
||||
oauth.linkedin.com.access = https://api.linkedin.com/uas/oauth/accessToken
|
||||
oauth.linkedin.com.authorize = https://api.linkedin.com/uas/oauth/authorize
|
||||
|
||||
sstatus.enabled = 0
|
||||
sstatus.servers = 127.0.0.1:11211
|
||||
sstatus.domains = google.com,twitter.com,facebook.com,linkedin.com
|
||||
|
|
|
@ -129,36 +129,49 @@ function ($, object, fn, dispatch, rdapi, accounts) {
|
|||
}));
|
||||
},
|
||||
|
||||
fetch: function () {
|
||||
fetch: function (pageData) {
|
||||
var acct = this.svcAccount;
|
||||
|
||||
accounts.getService(acct.domain, acct.userid,
|
||||
acct.username, fn.bind(this, function (svcData) {
|
||||
|
||||
var data = {
|
||||
username: acct.username,
|
||||
userid: acct.userid,
|
||||
account: JSON.stringify(svcData)
|
||||
};
|
||||
if (pageData) {
|
||||
data.pageData = JSON.stringify(pageData);
|
||||
}
|
||||
|
||||
rdapi('contacts/' + acct.domain, {
|
||||
type: 'POST',
|
||||
data: {
|
||||
username: acct.username,
|
||||
userid: acct.userid,
|
||||
startindex: 0,
|
||||
maxresults: 500,
|
||||
account: JSON.stringify(svcData)
|
||||
},
|
||||
data: data,
|
||||
//Only wait for 10 seconds, then give up.
|
||||
timeout: 10000,
|
||||
success: fn.bind(this, function (json) {
|
||||
//Transform data to a form usable by the front end.
|
||||
if (json && !json.error) {
|
||||
var entries = json.result.entry;
|
||||
var nextPageData = json.result.pageData;
|
||||
|
||||
this.getFormattedContacts(entries,
|
||||
fn.bind(this, function (contacts) {
|
||||
this.contacts = contacts;
|
||||
if (!pageData) {
|
||||
this.contacts = contacts;
|
||||
} else {
|
||||
this.contacts.push.apply(this.contacts, contacts);
|
||||
}
|
||||
this.lastUpdated = (new Date()).getTime();
|
||||
|
||||
this.toStore({
|
||||
list: this.contacts
|
||||
});
|
||||
if (nextPageData) {
|
||||
setTimeout(fn.bind(this, function(nextPageData) {
|
||||
this.fetch(nextPageData);
|
||||
}), 1, nextPageData);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче