mirror of https://github.com/zulip/zulip.git
[manual] Extend /api/v1/streams API endpoint.
Previously it only provided the list of all public streams; now it allows one to specify any union of some of the following: * all public streams * all streams the user subscribed to (the most relevant being the union of those two, which is what we want for the "streams" page). Or: * all streams in realm (superuser only) The manual task required is that when this is pushed to prod, we need to also deploy the new sync-public-streams version to zmirror. (imported from commit 27848b8bd136e2777f399b7d05b2fdcec35e4e21)
This commit is contained in:
parent
0a0cb9e70e
commit
f5f95e5f43
|
@ -43,4 +43,4 @@ parser.add_option_group(zulip.generate_option_group(parser))
|
|||
|
||||
client = zulip.init_from_options(options)
|
||||
|
||||
print client.get_public_streams()
|
||||
print client.get_streams(include_public=True, include_subscribed=False)
|
||||
|
|
|
@ -286,13 +286,16 @@ def _mk_events(event_types=None):
|
|||
return dict()
|
||||
return dict(event_types=event_types)
|
||||
|
||||
def _kwargs_to_dict(**kwargs):
|
||||
return kwargs
|
||||
|
||||
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_messages', method='GET', url='messages/latest', longpolling=True)
|
||||
Client._register('get_events', url='events', method='GET', longpolling=True, make_request=(lambda **kwargs: kwargs))
|
||||
Client._register('register', make_request=_mk_events)
|
||||
Client._register('get_profile', method='GET', url='users/me')
|
||||
Client._register('get_public_streams', method='GET', url='streams')
|
||||
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)
|
||||
|
|
|
@ -16,7 +16,7 @@ def fetch_public_streams():
|
|||
public_streams = set()
|
||||
|
||||
try:
|
||||
res = zulip_client.get_public_streams()
|
||||
res = zulip_client.get_streams(include_all_active=True)
|
||||
if res.get("result") == "success":
|
||||
streams = res["streams"]
|
||||
else:
|
||||
|
|
|
@ -22,6 +22,7 @@ from zerver.lib.cache import bounce_key_prefix_for_testing
|
|||
from zerver.lib.rate_limiter import clear_user_history
|
||||
from zerver.forms import not_mit_mailing_list
|
||||
|
||||
import base64
|
||||
from django.conf import settings
|
||||
import os
|
||||
import random
|
||||
|
@ -136,6 +137,12 @@ class AuthedTestCase(TestCase):
|
|||
API_KEYS[email] = get_user_profile_by_email(email).api_key
|
||||
return API_KEYS[email]
|
||||
|
||||
def api_auth(self, email):
|
||||
credentials = "%s:%s" % (email, self.get_api_key(email))
|
||||
return {
|
||||
'HTTP_AUTHORIZATION': 'Basic ' + base64.b64encode(credentials)
|
||||
}
|
||||
|
||||
def get_streams(self, email):
|
||||
"""
|
||||
Helper function to get the stream names for a user
|
||||
|
@ -1950,8 +1957,7 @@ class GetPublicStreamsTest(AuthedTestCase):
|
|||
email = 'hamlet@zulip.com'
|
||||
self.login(email)
|
||||
|
||||
api_key = self.get_api_key(email)
|
||||
result = self.client.post("/json/get_public_streams", {'email': email, 'api-key': api_key})
|
||||
result = self.client.post("/json/get_public_streams")
|
||||
|
||||
self.assert_json_success(result)
|
||||
json = ujson.loads(result.content)
|
||||
|
@ -1959,6 +1965,45 @@ class GetPublicStreamsTest(AuthedTestCase):
|
|||
self.assertIn("streams", json)
|
||||
self.assertIsInstance(json["streams"], list)
|
||||
|
||||
def test_public_streams_api(self):
|
||||
"""
|
||||
Ensure that get_public_streams successfully returns a list of streams
|
||||
"""
|
||||
email = 'hamlet@zulip.com'
|
||||
self.login(email)
|
||||
|
||||
# Check it correctly lists the user's subs with include_public=false
|
||||
result = self.client.get("/api/v1/streams?include_public=false", **self.api_auth(email))
|
||||
result2 = self.client.post("/json/subscriptions/list", {})
|
||||
|
||||
self.assert_json_success(result)
|
||||
json = ujson.loads(result.content)
|
||||
|
||||
self.assertIn("streams", json)
|
||||
self.assertIsInstance(json["streams"], list)
|
||||
|
||||
self.assert_json_success(result2)
|
||||
json2 = ujson.loads(result2.content)
|
||||
|
||||
self.assertEqual(sorted([s["name"] for s in json["streams"]]),
|
||||
sorted([s["name"] for s in json2["subscriptions"]]))
|
||||
|
||||
# Check it correctly lists all public streams with include_subscribed=false
|
||||
result = self.client.get("/api/v1/streams?include_public=true&include_subscribed=false",
|
||||
**self.api_auth(email))
|
||||
self.assert_json_success(result)
|
||||
|
||||
json = ujson.loads(result.content)
|
||||
all_streams = [stream.name for stream in
|
||||
Stream.objects.filter(realm=get_user_profile_by_email(email).realm)]
|
||||
self.assertEqual(sorted(s["name"] for s in json["streams"]),
|
||||
sorted(all_streams))
|
||||
|
||||
# Check non-superuser can't use include_all_active
|
||||
result = self.client.get("/api/v1/streams?include_all_active=true",
|
||||
**self.api_auth(email))
|
||||
self.assertEqual(result.status_code, 400)
|
||||
|
||||
class InviteOnlyStreamTest(AuthedTestCase):
|
||||
|
||||
def test_list_respects_invite_only_bit(self):
|
||||
|
|
|
@ -1293,25 +1293,52 @@ def api_get_public_streams(request, user_profile):
|
|||
def json_get_public_streams(request, user_profile):
|
||||
return get_public_streams_backend(request, user_profile)
|
||||
|
||||
def get_public_streams_backend(request, user_profile):
|
||||
if user_profile.realm.domain == "mit.edu" and not is_super_user_api(request):
|
||||
return json_error("User not authorized for this query")
|
||||
# By default, lists all streams that the user has access to --
|
||||
# i.e. public streams plus invite-only streams that the user is on
|
||||
@has_request_variables
|
||||
def get_streams_backend(request, user_profile,
|
||||
include_public=REQ(converter=json_to_bool, default=True),
|
||||
include_subscribed=REQ(converter=json_to_bool, default=True),
|
||||
include_all_active=REQ(converter=json_to_bool, default=False)):
|
||||
if include_all_active or (include_public and user_profile.realm.domain == "mit.edu"):
|
||||
if not is_super_user_api(request):
|
||||
return json_error("User not authorized for this query")
|
||||
|
||||
# Only get streams someone is currently subscribed to
|
||||
subs_filter = Subscription.objects.filter(active=True).values('recipient_id')
|
||||
stream_ids = Recipient.objects.filter(
|
||||
type=Recipient.STREAM, id__in=subs_filter).values('type_id')
|
||||
|
||||
# Start out with all active streams in the realm
|
||||
query = Stream.objects.filter(id__in = stream_ids, realm=user_profile.realm)
|
||||
if not (user_profile.realm.domain == "mit.edu" and is_super_user_api(request)):
|
||||
# We don't apply the `invite_only=False` filter when answering
|
||||
# the mit.edu API superuser, because the list of streams is
|
||||
# used as the list of all Zephyr classes to mirror, and we
|
||||
# want to include invite-only streams (aka zcrypted classes) in that
|
||||
query = query.filter(invite_only=False)
|
||||
|
||||
if not include_all_active:
|
||||
user_subs = Subscription.objects.select_related("recipient").filter(
|
||||
active=True, user_profile=user_profile,
|
||||
recipient__type=Recipient.STREAM)
|
||||
|
||||
if include_subscribed:
|
||||
recipient_check = Q(id__in=[sub.recipient.type_id for sub in user_subs])
|
||||
if include_public:
|
||||
invite_only_check = Q(invite_only=False)
|
||||
|
||||
if include_subscribed and include_public:
|
||||
query = query.filter(recipient_check | invite_only_check)
|
||||
elif include_public:
|
||||
query = query.filter(invite_only_check)
|
||||
elif include_subscribed:
|
||||
query = query.filter(recipient_check)
|
||||
else:
|
||||
# We're including nothing, so don't bother hitting the DB.
|
||||
query = []
|
||||
|
||||
streams = sorted({"name": stream.name} for stream in query)
|
||||
return json_success({"streams": streams})
|
||||
|
||||
def get_public_streams_backend(request, user_profile):
|
||||
return get_streams_backend(request, user_profile, include_public=True,
|
||||
include_subscribed=False, include_all_active=False)
|
||||
|
||||
@authenticated_api_view
|
||||
def api_list_subscriptions(request, user_profile):
|
||||
return list_subscriptions_backend(request, user_profile)
|
||||
|
|
|
@ -156,7 +156,7 @@ v1_api_and_json_patterns = patterns('zerver.views',
|
|||
url(r'^messages/flags$', 'rest_dispatch',
|
||||
{'POST': 'update_message_flags'}),
|
||||
url(r'^streams$', 'rest_dispatch',
|
||||
{'GET': 'get_public_streams_backend'}),
|
||||
{'GET': 'get_streams_backend'}),
|
||||
# GET returns "stream info" (undefined currently?), HEAD returns whether stream exists (200 or 404)
|
||||
url(r'^streams/(?P<stream_name>.*)/members$', 'rest_dispatch',
|
||||
{'GET': 'get_subscribers_backend'}),
|
||||
|
|
Loading…
Reference in New Issue