Use Idempotency-Key header when posting toots

pull/70/head
Ivan Habunek 2018-06-13 13:22:52 +02:00
rodzic 8f93b255ad
commit 025d8dde09
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: CDBD63C43A30BB95
4 zmienionych plików z 24 dodań i 7 usunięć

Wyświetl plik

@ -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)**

Wyświetl plik

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

Wyświetl plik

@ -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):

Wyświetl plik

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