api_docs: Prep work to document zilencer endpoints.

Until now we were not documenting bouncer's REST API endpoints.

We plan to document the newly introduced "remotes/push/e2ee/register"
and "remotes/push/e2ee/notify" endpoints.

This commit does the prep work for documenting bouncer endpoints:
* mark the older endpoints related to sending non-E2EE push
  notifications as "intentionally_undocumented" - we'll remove
  them in future.
* the remaining endpoints are marked pending-to-document with
  helpful comments.
This commit is contained in:
Prakhar Pratyush
2025-08-27 13:46:01 +05:30
committed by Tim Abbott
parent 9785cb4a09
commit 062a736097
3 changed files with 51 additions and 15 deletions

View File

@@ -30,6 +30,7 @@ from zerver.openapi.openapi import (
validate_schema,
)
from zerver.tornado.views import get_events, get_events_backend
from zilencer.auth import remote_server_dispatch
TEST_ENDPOINT = "/messages/{message_id}"
TEST_METHOD = "patch"
@@ -260,6 +261,18 @@ class OpenAPIArgumentsTest(ZulipTestCase):
# Zulip outgoing webhook payload
"/zulip-outgoing-webhook",
"/jwt/fetch_api_key",
#### Bouncer endpoints
# Higher priority to document
"/remotes/push/e2ee/register",
"/remotes/push/e2ee/notify",
# Lower priority to document
"/remotes/server/register",
"/remotes/server/register/transfer",
"/remotes/server/register/verify_challenge",
"/remotes/server/deactivate",
"/remotes/server/analytics",
"/remotes/server/analytics/status",
"/remotes/server/billing",
}
# Endpoints in the API documentation that don't use rest_dispatch
@@ -530,8 +543,8 @@ so maybe we shouldn't include it in pending_endpoints.
with the arguments declared in our API documentation
for every API endpoint in Zulip.
First, we import the fancy-Django version of zproject/urls.py
by doing this, each typed_endpoint wrapper around each
First, we import the fancy-Django version of zproject/urls.py and
zilencer/urls.py. By doing this, each typed_endpoint wrapper around each
imported view function gets called to generate the wrapped
view function and thus filling the global arguments_map variable.
Basically, we're exploiting code execution during import.
@@ -546,15 +559,21 @@ so maybe we shouldn't include it in pending_endpoints.
in code.
"""
from zilencer import urls as zilencer_urlconf
from zproject import urls as urlconf
# We loop through all the API patterns, looking in particular
# for those using the rest_dispatch decorator; we then parse
# its mapping of (HTTP_METHOD -> FUNCTION).
for p in urlconf.v1_api_and_json_patterns + urlconf.v1_api_mobile_patterns:
# for those using the rest_dispatch or remote_server_dispatch decorator;
# we then parse its mapping of (HTTP_METHOD -> FUNCTION).
for p in (
urlconf.v1_api_and_json_patterns
+ urlconf.v1_api_mobile_patterns
+ zilencer_urlconf.v1_api_bouncer_patterns
):
methods_endpoints: dict[str, Any] = {}
if p.callback is not rest_dispatch:
# Endpoints not using rest_dispatch don't have extra data.
if p.callback not in [rest_dispatch, remote_server_dispatch]:
# Endpoints not using rest_dispatch or remote_server_dispatch
# don't have extra data.
if str(p.pattern) in self.documented_post_only_endpoints:
methods_endpoints = dict(POST=p.callback)
else:

View File

@@ -172,6 +172,7 @@ def remote_server_dispatch(request: HttpRequest, /, **kwargs: Any) -> HttpRespon
def remote_server_path(
route: str,
**handlers: Callable[Concatenate[HttpRequest, RemoteZulipServer, ParamT], HttpResponse],
**handlers: Callable[Concatenate[HttpRequest, RemoteZulipServer, ParamT], HttpResponse]
| tuple[Callable[Concatenate[HttpRequest, RemoteZulipServer, ParamT], HttpResponse], set[str]],
) -> URLPattern:
return path(route, remote_server_dispatch, handlers)

View File

@@ -24,16 +24,31 @@ from zilencer.views import (
i18n_urlpatterns: Any = []
# Zilencer views following the REST API style
# The endpoints marked "intentionally_undocumented" are part of the older system
# for sending non-E2EE push notifications, and will be removed in the future.
push_bouncer_patterns = [
remote_server_path("remotes/push/register", POST=register_remote_push_device),
remote_server_path(
"remotes/push/register", POST=(register_remote_push_device, {"intentionally_undocumented"})
),
remote_server_path(
"remotes/push/e2ee/register", POST=register_remote_push_device_for_e2ee_push_notification
),
remote_server_path("remotes/push/unregister", POST=unregister_remote_push_device),
remote_server_path("remotes/push/unregister/all", POST=unregister_all_remote_push_devices),
remote_server_path("remotes/push/notify", POST=remote_server_notify_push),
remote_server_path(
"remotes/push/unregister",
POST=(unregister_remote_push_device, {"intentionally_undocumented"}),
),
remote_server_path(
"remotes/push/unregister/all",
POST=(unregister_all_remote_push_devices, {"intentionally_undocumented"}),
),
remote_server_path(
"remotes/push/notify", POST=(remote_server_notify_push, {"intentionally_undocumented"})
),
remote_server_path("remotes/push/e2ee/notify", POST=remote_server_send_e2ee_push_notification),
remote_server_path("remotes/push/test_notification", POST=remote_server_send_test_notification),
remote_server_path(
"remotes/push/test_notification",
POST=(remote_server_send_test_notification, {"intentionally_undocumented"}),
),
# Push signup doesn't use the REST API, since there's no auth.
path("remotes/server/register", register_remote_server),
path("remotes/server/register/transfer", transfer_remote_server_registration),
@@ -49,7 +64,8 @@ push_bouncer_patterns = [
billing_patterns = [remote_server_path("remotes/server/billing", POST=remote_realm_billing_entry)]
v1_api_bouncer_patterns = push_bouncer_patterns + billing_patterns
urlpatterns = [
path("api/v1/", include(push_bouncer_patterns)),
path("api/v1/", include(billing_patterns)),
path("api/v1/", include(v1_api_bouncer_patterns)),
]