Use Idempotency-Key header when posting toots
This commit is contained in:
parent
8f93b255ad
commit
025d8dde09
4 changed files with 24 additions and 7 deletions
|
@ -4,6 +4,8 @@ Changelog
|
||||||
**0.19.0 (TBA)**
|
**0.19.0 (TBA)**
|
||||||
|
|
||||||
* Add support for replying to a toot (#6)
|
* Add support for replying to a toot (#6)
|
||||||
|
* Use Idempotency-Key header to prevent multiple toots being posted if request
|
||||||
|
is retried
|
||||||
|
|
||||||
**0.18.0 (2018-06-12)**
|
**0.18.0 (2018-06-12)**
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
import io
|
import io
|
||||||
import pytest
|
import pytest
|
||||||
import re
|
import re
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from toot import console, User, App, http
|
from toot import console, User, App, http
|
||||||
|
@ -13,6 +15,8 @@ from tests.utils import MockResponse
|
||||||
app = App('habunek.com', 'https://habunek.com', 'foo', 'bar')
|
app = App('habunek.com', 'https://habunek.com', 'foo', 'bar')
|
||||||
user = User('habunek.com', 'ivan@habunek.com', 'xxx')
|
user = User('habunek.com', 'ivan@habunek.com', 'xxx')
|
||||||
|
|
||||||
|
MockUuid = namedtuple("MockUuid", ["hex"])
|
||||||
|
|
||||||
|
|
||||||
def uncolorize(text):
|
def uncolorize(text):
|
||||||
"""Remove ANSI color sequences from a string"""
|
"""Remove ANSI color sequences from a string"""
|
||||||
|
@ -25,8 +29,10 @@ def test_print_usage(capsys):
|
||||||
assert "toot - a Mastodon CLI client" in out
|
assert "toot - a Mastodon CLI client" in out
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('uuid.uuid4')
|
||||||
@mock.patch('toot.http.post')
|
@mock.patch('toot.http.post')
|
||||||
def test_post_defaults(mock_post, capsys):
|
def test_post_defaults(mock_post, mock_uuid, capsys):
|
||||||
|
mock_uuid.return_value = MockUuid("rock-on")
|
||||||
mock_post.return_value = MockResponse({
|
mock_post.return_value = MockResponse({
|
||||||
'url': 'https://habunek.com/@ihabunek/1234567890'
|
'url': 'https://habunek.com/@ihabunek/1234567890'
|
||||||
})
|
})
|
||||||
|
@ -40,7 +46,7 @@ def test_post_defaults(mock_post, capsys):
|
||||||
'sensitive': False,
|
'sensitive': False,
|
||||||
'spoiler_text': None,
|
'spoiler_text': None,
|
||||||
'in_reply_to_id': None,
|
'in_reply_to_id': None,
|
||||||
})
|
}, headers={"Idempotency-Key": "rock-on"})
|
||||||
|
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert 'Toot posted' in out
|
assert 'Toot posted' in out
|
||||||
|
@ -48,8 +54,10 @@ def test_post_defaults(mock_post, capsys):
|
||||||
assert not err
|
assert not err
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('uuid.uuid4')
|
||||||
@mock.patch('toot.http.post')
|
@mock.patch('toot.http.post')
|
||||||
def test_post_with_options(mock_post, capsys):
|
def test_post_with_options(mock_post, mock_uuid, capsys):
|
||||||
|
mock_uuid.return_value = MockUuid("up-the-irons")
|
||||||
args = [
|
args = [
|
||||||
'Hello world',
|
'Hello world',
|
||||||
'--visibility', 'unlisted',
|
'--visibility', 'unlisted',
|
||||||
|
@ -71,7 +79,7 @@ def test_post_with_options(mock_post, capsys):
|
||||||
'sensitive': True,
|
'sensitive': True,
|
||||||
'spoiler_text': "Spoiler!",
|
'spoiler_text': "Spoiler!",
|
||||||
'in_reply_to_id': 123,
|
'in_reply_to_id': 123,
|
||||||
})
|
}, headers={"Idempotency-Key": "up-the-irons"})
|
||||||
|
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert 'Toot posted' in out
|
assert 'Toot posted' in out
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import uuid
|
||||||
|
|
||||||
from urllib.parse import urlparse, urlencode, quote
|
from urllib.parse import urlparse, urlencode, quote
|
||||||
|
|
||||||
|
@ -90,6 +91,11 @@ def post_status(
|
||||||
Posts a new status.
|
Posts a new status.
|
||||||
https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#posting-a-new-status
|
https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#posting-a-new-status
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Idempotency key assures the same status is not posted multiple times
|
||||||
|
# if the request is retried.
|
||||||
|
headers = {"Idempotency-Key": uuid.uuid4().hex}
|
||||||
|
|
||||||
return http.post(app, user, '/api/v1/statuses', {
|
return http.post(app, user, '/api/v1/statuses', {
|
||||||
'status': status,
|
'status': status,
|
||||||
'media_ids[]': media_ids,
|
'media_ids[]': media_ids,
|
||||||
|
@ -97,7 +103,7 @@ def post_status(
|
||||||
'sensitive': sensitive,
|
'sensitive': sensitive,
|
||||||
'spoiler_text': spoiler_text,
|
'spoiler_text': spoiler_text,
|
||||||
'in_reply_to_id': in_reply_to_id,
|
'in_reply_to_id': in_reply_to_id,
|
||||||
}).json()
|
}, headers=headers).json()
|
||||||
|
|
||||||
|
|
||||||
def timeline_home(app, user):
|
def timeline_home(app, user):
|
||||||
|
|
|
@ -58,9 +58,10 @@ def anon_get(url, params=None):
|
||||||
return process_response(response)
|
return process_response(response)
|
||||||
|
|
||||||
|
|
||||||
def post(app, user, url, data=None, files=None, allow_redirects=True):
|
def post(app, user, url, data=None, files=None, allow_redirects=True, headers={}):
|
||||||
url = app.base_url + url
|
url = app.base_url + url
|
||||||
headers = {"Authorization": "Bearer " + user.access_token}
|
|
||||||
|
headers["Authorization"] = "Bearer " + user.access_token
|
||||||
|
|
||||||
request = Request('POST', url, headers, files, data)
|
request = Request('POST', url, headers, files, data)
|
||||||
response = send_request(request, allow_redirects)
|
response = send_request(request, allow_redirects)
|
||||||
|
|
Loading…
Reference in a new issue