mirror of https://github.com/zulip/zulip.git
Simplify, document, and fix the API code.
We used to create endpoints with Client._register. Now we now have explicit methods for the endpoints. This allows us to add docstrings and stricter mypy annotations. This fix also introduces a call_endpoint() method that avoids the need for manually building urls with API_VERSTRING when you know the URL pattern of the endpoint you want to hit (and when the API doesn't have a convenient wrapper). I fixed a bug with create_users where it now uses PUT instead of POST. I also removed client.export(), which was just broken. I had to change recent-messages and zulip-export, which were using client.do_api_query and Client._register. Now it's easier to just call client.call_endpoint() for situations where our API doesn't have convenient wrappers, so that's what I did with those scripts.
This commit is contained in:
parent
bade6f496b
commit
efc2d1a675
|
@ -35,7 +35,7 @@ Example: recent-messages --count=101 --user=username@example.com --api-key=a0b1c
|
|||
|
||||
You can omit --user and --api-key arguments if you have a properly set up ~/.zuliprc
|
||||
"""
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||
import zulip
|
||||
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
|
@ -45,7 +45,7 @@ parser.add_option_group(zulip.generate_option_group(parser))
|
|||
|
||||
client = zulip.init_from_options(options)
|
||||
|
||||
req = {
|
||||
request = {
|
||||
'narrow': [["stream", "Denmark"]],
|
||||
'num_before': options.count,
|
||||
'num_after': 0,
|
||||
|
@ -53,7 +53,12 @@ req = {
|
|||
'apply_markdown': False
|
||||
}
|
||||
|
||||
old_messages = client.do_api_query(req, zulip.API_VERSTRING + 'messages', method='GET')
|
||||
old_messages = client.call_endpoint(
|
||||
url='messages',
|
||||
method='GET',
|
||||
request=request,
|
||||
)
|
||||
|
||||
if 'messages' in old_messages:
|
||||
for message in old_messages['messages']:
|
||||
print(json.dumps(message, indent=4))
|
||||
|
|
|
@ -38,7 +38,7 @@ from six.moves.configparser import SafeConfigParser
|
|||
from six.moves import urllib
|
||||
import logging
|
||||
import six
|
||||
from typing import Any, Callable, Dict, Mapping, Optional, Tuple, Union
|
||||
from typing import Any, Callable, Dict, Mapping, Optional, Tuple, Union, Iterable
|
||||
|
||||
__version__ = "0.2.5"
|
||||
|
||||
|
@ -290,7 +290,7 @@ class Client(object):
|
|||
vendor_version=vendor_version,
|
||||
)
|
||||
|
||||
def do_api_query(self, orig_request, url, method="POST", longpolling = False):
|
||||
def do_api_query(self, orig_request, url, method="POST", longpolling=False):
|
||||
# type: (Mapping[str, Any], str, str, bool) -> Dict[str, Any]
|
||||
request = {}
|
||||
|
||||
|
@ -402,26 +402,11 @@ class Client(object):
|
|||
return {'msg': "Unexpected error from the server", "result": "http-error",
|
||||
"status_code": res.status_code}
|
||||
|
||||
@classmethod
|
||||
def _register(cls, name, url=None, make_request=None,
|
||||
method="POST", computed_url=None, **query_kwargs):
|
||||
# type: (Any, str, Optional[Callable], str, Optional[Callable], **Any) -> Any
|
||||
if url is None:
|
||||
url = name
|
||||
if make_request is None:
|
||||
def make_request(**kwargs):
|
||||
# type: (**Any) -> Any
|
||||
return kwargs.get("request", {})
|
||||
def call(self, *args, **kwargs):
|
||||
# type: (*Any, **Any) -> str
|
||||
request = make_request(*args, **kwargs)
|
||||
if computed_url is not None:
|
||||
req_url = computed_url(request)
|
||||
else:
|
||||
req_url = url
|
||||
return self.do_api_query(request, API_VERSTRING + req_url, method=method, **query_kwargs)
|
||||
call.__name__ = name
|
||||
setattr(cls, name, call)
|
||||
def call_endpoint(self, url=None, method="POST", request=None, longpolling=False):
|
||||
# type: (str, str, Dict[str, Any], bool) -> Dict[str, Any]
|
||||
if request is None:
|
||||
request = dict()
|
||||
return self.do_api_query(request, API_VERSTRING + url, method=method)
|
||||
|
||||
def call_on_each_event(self, callback, event_types=None, narrow=None):
|
||||
# type: (Callable, Optional[List[str]], Any) -> None
|
||||
|
@ -485,31 +470,193 @@ class Client(object):
|
|||
callback(event['message'])
|
||||
self.call_on_each_event(event_callback, ['message'])
|
||||
|
||||
def _mk_subs(streams, **kwargs):
|
||||
# type: (str, **Any ) -> Dict[str, str]
|
||||
result = kwargs
|
||||
result['subscriptions'] = streams
|
||||
return result
|
||||
def send_message(self, message_data):
|
||||
# type: (Dict[str, Any]) -> Dict[str, Any]
|
||||
'''
|
||||
See api/examples/send-message for example usage.
|
||||
'''
|
||||
return self.call_endpoint(
|
||||
url='messages',
|
||||
request=message_data,
|
||||
)
|
||||
|
||||
def _mk_rm_subs(streams):
|
||||
# type: (str) -> Dict[str, str]
|
||||
return {'delete': streams}
|
||||
def update_message(self, message_data):
|
||||
# type: (Dict[str, Any]) -> Dict[str, Any]
|
||||
'''
|
||||
See api/examples/edit-message for example usage.
|
||||
'''
|
||||
return self.call_endpoint(
|
||||
url='messages',
|
||||
method='PATCH',
|
||||
request=message_data,
|
||||
)
|
||||
|
||||
def _mk_deregister(queue_id):
|
||||
# type: (str) -> Dict[str, str]
|
||||
return {'queue_id': queue_id}
|
||||
def get_events(self, **request):
|
||||
# type: (**Any) -> Dict[str, Any]
|
||||
'''
|
||||
See the register() method for example usage.
|
||||
'''
|
||||
return self.call_endpoint(
|
||||
url='events',
|
||||
method='GET',
|
||||
longpolling=True,
|
||||
request=request,
|
||||
)
|
||||
|
||||
def _mk_events(event_types=None, narrow=None):
|
||||
# type: (Any, List[None]) -> Dict[Any, List[None]]
|
||||
if event_types is None:
|
||||
return dict()
|
||||
if narrow is None:
|
||||
narrow = []
|
||||
return dict(event_types=event_types, narrow=narrow)
|
||||
def register(self, event_types=None, narrow=None, **kwargs):
|
||||
# type: (Iterable[str], Any, **Any) -> Dict[str, Any]
|
||||
'''
|
||||
Example usage:
|
||||
|
||||
def _kwargs_to_dict(**kwargs):
|
||||
# type: (**Any) -> Any
|
||||
return kwargs
|
||||
>>> client.register(['message'])
|
||||
{u'msg': u'', u'max_message_id': 112, u'last_event_id': -1, u'result': u'success', u'queue_id': u'1482093786:2'}
|
||||
>>> client.get_events(queue_id='1482093786:2', last_event_id=0)
|
||||
{...}
|
||||
'''
|
||||
|
||||
if narrow is None:
|
||||
narrow = []
|
||||
|
||||
request = dict(
|
||||
event_types=event_types,
|
||||
narrow=narrow,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
return self.call_endpoint(
|
||||
url='register',
|
||||
request=request,
|
||||
)
|
||||
|
||||
def deregister(self, queue_id):
|
||||
# type: (str) -> Dict[str, Any]
|
||||
'''
|
||||
Example usage:
|
||||
|
||||
>>> client.register(['message'])
|
||||
{u'msg': u'', u'max_message_id': 113, u'last_event_id': -1, u'result': u'success', u'queue_id': u'1482093786:3'}
|
||||
>>> client.deregister('1482093786:3')
|
||||
{u'msg': u'', u'result': u'success'}
|
||||
'''
|
||||
request = dict(queue_id=queue_id)
|
||||
|
||||
return self.call_endpoint(
|
||||
url="events",
|
||||
method="DELETE",
|
||||
request=request,
|
||||
)
|
||||
|
||||
def get_profile(self, request=None):
|
||||
# type: (Dict[str, Any]) -> Dict[str, Any]
|
||||
'''
|
||||
Example usage:
|
||||
|
||||
>>> client.get_profile()
|
||||
{u'user_id': 5, u'full_name': u'Iago', u'short_name': u'iago', ...}
|
||||
'''
|
||||
return self.call_endpoint(
|
||||
url='users/me',
|
||||
method='GET',
|
||||
request=request,
|
||||
)
|
||||
|
||||
def get_streams(self, **request):
|
||||
# type: (**Any) -> Dict[str, Any]
|
||||
'''
|
||||
See api/examples/get-public-streams for example usage.
|
||||
'''
|
||||
return self.call_endpoint(
|
||||
url='streams',
|
||||
method='GET',
|
||||
request=request,
|
||||
)
|
||||
|
||||
def get_members(self, request=None):
|
||||
# type: (Dict[str, Any]) -> Dict[str, Any]
|
||||
'''
|
||||
See api/examples/list-members for example usage.
|
||||
'''
|
||||
return self.call_endpoint(
|
||||
url='users',
|
||||
method='GET',
|
||||
request=request,
|
||||
)
|
||||
|
||||
def list_subscriptions(self, request=None):
|
||||
# type: (Dict[str, Any]) -> Dict[str, Any]
|
||||
'''
|
||||
See api/examples/list-subscriptions for example usage.
|
||||
'''
|
||||
return self.call_endpoint(
|
||||
url='users/me/subscriptions',
|
||||
method='GET',
|
||||
request=request,
|
||||
)
|
||||
|
||||
def add_subscriptions(self, streams, **kwargs):
|
||||
# type: (Iterable[Dict[str, Any]], **Any) -> Dict[str, Any]
|
||||
'''
|
||||
See api/examples/subscribe for example usage.
|
||||
'''
|
||||
request = dict(
|
||||
subscriptions=streams,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
return self.call_endpoint(
|
||||
url='users/me/subscriptions',
|
||||
request=request,
|
||||
)
|
||||
|
||||
def remove_subscriptions(self, streams):
|
||||
# type: (Iterable[str]) -> Dict[str, Any]
|
||||
'''
|
||||
See api/examples/unsubscribe for example usage.
|
||||
'''
|
||||
request = dict(delete=streams)
|
||||
return self.call_endpoint(
|
||||
url='users/me/subscriptions',
|
||||
method='PATCH',
|
||||
request=request,
|
||||
)
|
||||
|
||||
def get_subscribers(self, **request):
|
||||
# type: (**Any) -> Dict[str, Any]
|
||||
'''
|
||||
Example usage: client.get_subscribers(stream='devel')
|
||||
'''
|
||||
stream = urllib.parse.quote(request['stream'], safe='')
|
||||
url = 'streams/%s/members' % (stream,)
|
||||
return self.call_endpoint(
|
||||
url=url,
|
||||
method='GET',
|
||||
request=request,
|
||||
)
|
||||
|
||||
def render_message(self, request=None):
|
||||
# type: (Dict[str, Any]) -> Dict[str, Any]
|
||||
'''
|
||||
Example usage:
|
||||
|
||||
>>> client.render_message(request=dict(content='foo **bar**'))
|
||||
{u'msg': u'', u'rendered': u'<p>foo <strong>bar</strong></p>', u'result': u'success'}
|
||||
'''
|
||||
return self.call_endpoint(
|
||||
url='messages/render',
|
||||
method='GET',
|
||||
request=request,
|
||||
)
|
||||
|
||||
def create_user(self, request=None):
|
||||
# type: (Dict[str, Any]) -> Dict[str, Any]
|
||||
'''
|
||||
See api/examples/create-user for example usage.
|
||||
'''
|
||||
return self.call_endpoint(
|
||||
method='PUT',
|
||||
url='users',
|
||||
request=request,
|
||||
)
|
||||
|
||||
class ZulipStream(object):
|
||||
"""
|
||||
|
@ -534,21 +681,3 @@ class ZulipStream(object):
|
|||
def flush(self):
|
||||
# type: () -> None
|
||||
pass
|
||||
|
||||
Client._register('send_message', url='messages', make_request=(lambda request: request))
|
||||
Client._register('update_message', method='PATCH', url='messages', make_request=(lambda request: request))
|
||||
Client._register('get_events', url='events', method='GET', longpolling=True, make_request=(lambda **kwargs: kwargs))
|
||||
Client._register('register', make_request=_mk_events)
|
||||
Client._register('export', method='GET', url='export')
|
||||
Client._register('deregister', url="events", method="DELETE", make_request=_mk_deregister)
|
||||
Client._register('get_profile', method='GET', url='users/me')
|
||||
Client._register('get_streams', method='GET', url='streams', make_request=_kwargs_to_dict)
|
||||
Client._register('get_members', method='GET', url='users')
|
||||
Client._register('list_subscriptions', method='GET', url='users/me/subscriptions')
|
||||
Client._register('add_subscriptions', url='users/me/subscriptions', make_request=_mk_subs)
|
||||
Client._register('remove_subscriptions', method='PATCH', url='users/me/subscriptions', make_request=_mk_rm_subs)
|
||||
Client._register('get_subscribers', method='GET',
|
||||
computed_url=lambda request: 'streams/%s/members' % (urllib.parse.quote(request['stream'], safe=''),),
|
||||
make_request=_kwargs_to_dict)
|
||||
Client._register('render_message', method='GET', url='messages/render')
|
||||
Client._register('create_user', method='POST', url='users')
|
||||
|
|
|
@ -53,16 +53,24 @@ if options.stream == "":
|
|||
|
||||
client.add_subscriptions([{"name": options.stream}])
|
||||
queue = client.register(event_types=['message'])
|
||||
client._register('get_old_messages', method='GET', url='messages')
|
||||
max_id = queue['max_message_id']
|
||||
messages = []
|
||||
request = {
|
||||
'anchor': 0,
|
||||
'num_before': 0,
|
||||
'num_after': max_id,
|
||||
'narrow': [{'operator': 'stream',
|
||||
'operand': options.stream}],
|
||||
'apply_markdown': False,
|
||||
}
|
||||
|
||||
print("Fetching messages...")
|
||||
result = client.get_old_messages({'anchor': 0,
|
||||
'num_before': 0,
|
||||
'num_after': max_id,
|
||||
'narrow': [{'operator': 'stream',
|
||||
'operand': options.stream}],
|
||||
'apply_markdown': False})
|
||||
result = client.call_endpoint(
|
||||
url='messages',
|
||||
method='GET',
|
||||
request=request,
|
||||
)
|
||||
|
||||
if result['result'] != 'success':
|
||||
print("Unfortunately, there was an error fetching some old messages.")
|
||||
print(result)
|
||||
|
|
Loading…
Reference in New Issue