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
|
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
|
import zulip
|
||||||
|
|
||||||
parser = optparse.OptionParser(usage=usage)
|
parser = optparse.OptionParser(usage=usage)
|
||||||
|
@ -45,7 +45,7 @@ parser.add_option_group(zulip.generate_option_group(parser))
|
||||||
|
|
||||||
client = zulip.init_from_options(options)
|
client = zulip.init_from_options(options)
|
||||||
|
|
||||||
req = {
|
request = {
|
||||||
'narrow': [["stream", "Denmark"]],
|
'narrow': [["stream", "Denmark"]],
|
||||||
'num_before': options.count,
|
'num_before': options.count,
|
||||||
'num_after': 0,
|
'num_after': 0,
|
||||||
|
@ -53,7 +53,12 @@ req = {
|
||||||
'apply_markdown': False
|
'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:
|
if 'messages' in old_messages:
|
||||||
for message in old_messages['messages']:
|
for message in old_messages['messages']:
|
||||||
print(json.dumps(message, indent=4))
|
print(json.dumps(message, indent=4))
|
||||||
|
|
|
@ -38,7 +38,7 @@ from six.moves.configparser import SafeConfigParser
|
||||||
from six.moves import urllib
|
from six.moves import urllib
|
||||||
import logging
|
import logging
|
||||||
import six
|
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"
|
__version__ = "0.2.5"
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ class Client(object):
|
||||||
vendor_version=vendor_version,
|
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]
|
# type: (Mapping[str, Any], str, str, bool) -> Dict[str, Any]
|
||||||
request = {}
|
request = {}
|
||||||
|
|
||||||
|
@ -402,26 +402,11 @@ class Client(object):
|
||||||
return {'msg': "Unexpected error from the server", "result": "http-error",
|
return {'msg': "Unexpected error from the server", "result": "http-error",
|
||||||
"status_code": res.status_code}
|
"status_code": res.status_code}
|
||||||
|
|
||||||
@classmethod
|
def call_endpoint(self, url=None, method="POST", request=None, longpolling=False):
|
||||||
def _register(cls, name, url=None, make_request=None,
|
# type: (str, str, Dict[str, Any], bool) -> Dict[str, Any]
|
||||||
method="POST", computed_url=None, **query_kwargs):
|
if request is None:
|
||||||
# type: (Any, str, Optional[Callable], str, Optional[Callable], **Any) -> Any
|
request = dict()
|
||||||
if url is None:
|
return self.do_api_query(request, API_VERSTRING + url, method=method)
|
||||||
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_on_each_event(self, callback, event_types=None, narrow=None):
|
def call_on_each_event(self, callback, event_types=None, narrow=None):
|
||||||
# type: (Callable, Optional[List[str]], Any) -> None
|
# type: (Callable, Optional[List[str]], Any) -> None
|
||||||
|
@ -485,31 +470,193 @@ class Client(object):
|
||||||
callback(event['message'])
|
callback(event['message'])
|
||||||
self.call_on_each_event(event_callback, ['message'])
|
self.call_on_each_event(event_callback, ['message'])
|
||||||
|
|
||||||
def _mk_subs(streams, **kwargs):
|
def send_message(self, message_data):
|
||||||
# type: (str, **Any ) -> Dict[str, str]
|
# type: (Dict[str, Any]) -> Dict[str, Any]
|
||||||
result = kwargs
|
'''
|
||||||
result['subscriptions'] = streams
|
See api/examples/send-message for example usage.
|
||||||
return result
|
'''
|
||||||
|
return self.call_endpoint(
|
||||||
|
url='messages',
|
||||||
|
request=message_data,
|
||||||
|
)
|
||||||
|
|
||||||
def _mk_rm_subs(streams):
|
def update_message(self, message_data):
|
||||||
# type: (str) -> Dict[str, str]
|
# type: (Dict[str, Any]) -> Dict[str, Any]
|
||||||
return {'delete': streams}
|
'''
|
||||||
|
See api/examples/edit-message for example usage.
|
||||||
|
'''
|
||||||
|
return self.call_endpoint(
|
||||||
|
url='messages',
|
||||||
|
method='PATCH',
|
||||||
|
request=message_data,
|
||||||
|
)
|
||||||
|
|
||||||
def _mk_deregister(queue_id):
|
def get_events(self, **request):
|
||||||
# type: (str) -> Dict[str, str]
|
# type: (**Any) -> Dict[str, Any]
|
||||||
return {'queue_id': queue_id}
|
'''
|
||||||
|
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):
|
def register(self, event_types=None, narrow=None, **kwargs):
|
||||||
# type: (Any, List[None]) -> Dict[Any, List[None]]
|
# type: (Iterable[str], Any, **Any) -> Dict[str, Any]
|
||||||
if event_types is None:
|
'''
|
||||||
return dict()
|
Example usage:
|
||||||
if narrow is None:
|
|
||||||
narrow = []
|
|
||||||
return dict(event_types=event_types, narrow=narrow)
|
|
||||||
|
|
||||||
def _kwargs_to_dict(**kwargs):
|
>>> client.register(['message'])
|
||||||
# type: (**Any) -> Any
|
{u'msg': u'', u'max_message_id': 112, u'last_event_id': -1, u'result': u'success', u'queue_id': u'1482093786:2'}
|
||||||
return kwargs
|
>>> 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):
|
class ZulipStream(object):
|
||||||
"""
|
"""
|
||||||
|
@ -534,21 +681,3 @@ class ZulipStream(object):
|
||||||
def flush(self):
|
def flush(self):
|
||||||
# type: () -> None
|
# type: () -> None
|
||||||
pass
|
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}])
|
client.add_subscriptions([{"name": options.stream}])
|
||||||
queue = client.register(event_types=['message'])
|
queue = client.register(event_types=['message'])
|
||||||
client._register('get_old_messages', method='GET', url='messages')
|
|
||||||
max_id = queue['max_message_id']
|
max_id = queue['max_message_id']
|
||||||
messages = []
|
messages = []
|
||||||
|
request = {
|
||||||
|
'anchor': 0,
|
||||||
|
'num_before': 0,
|
||||||
|
'num_after': max_id,
|
||||||
|
'narrow': [{'operator': 'stream',
|
||||||
|
'operand': options.stream}],
|
||||||
|
'apply_markdown': False,
|
||||||
|
}
|
||||||
|
|
||||||
print("Fetching messages...")
|
print("Fetching messages...")
|
||||||
result = client.get_old_messages({'anchor': 0,
|
result = client.call_endpoint(
|
||||||
'num_before': 0,
|
url='messages',
|
||||||
'num_after': max_id,
|
method='GET',
|
||||||
'narrow': [{'operator': 'stream',
|
request=request,
|
||||||
'operand': options.stream}],
|
)
|
||||||
'apply_markdown': False})
|
|
||||||
if result['result'] != 'success':
|
if result['result'] != 'success':
|
||||||
print("Unfortunately, there was an error fetching some old messages.")
|
print("Unfortunately, there was an error fetching some old messages.")
|
||||||
print(result)
|
print(result)
|
||||||
|
|
Loading…
Reference in New Issue