Merge branch 'develop' into bug/642873

Conflicts:
	linkdrop/controllers/contacts.py
	linkdrop/tests/services/test_playback.py
	test.ini
This commit is contained in:
Rob Miller 2011-04-21 13:44:28 -07:00
Родитель c988616ae2 113cc88aaa
Коммит f593a315a0
31 изменённых файлов: 296 добавлений и 25 удалений

Просмотреть файл

@ -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,
}

Просмотреть файл

@ -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);
}
})
);
}