mirror of
https://github.com/zulip/zulip.git
synced 2026-06-03 21:01:43 +08:00
devices: Add an endpoint to remove Device records.
This commit adds a `POST /remove_client_device` endpoint to remove a Device record. Helps mobile clients to cleanup their Device records when user logs out. Signed-off-by: Prakhar Pratyush <prakhar@zulip.com>
This commit is contained in:
parent
1b340c6b74
commit
b1da50bd7f
@ -20,6 +20,11 @@ format used by the Zulip server that they are interacting with.
|
||||
|
||||
## Changes in Zulip 12.0
|
||||
|
||||
**Feature level 470**
|
||||
|
||||
* [`POST /remove_client_device`](/api/remove-client-device):
|
||||
Added a new endpoint to remove a registered device.
|
||||
|
||||
**Feature level 469**
|
||||
|
||||
* `PATCH /realm`, [`POST /register`](/api/register-queue),
|
||||
|
||||
@ -161,6 +161,7 @@
|
||||
* [Fetch an API key (production)](/api/fetch-api-key)
|
||||
* [Fetch an API key (development only)](/api/dev-fetch-api-key)
|
||||
* [Register a logged-in device](/api/register-client-device)
|
||||
* [Remove a registered device](/api/remove-client-device)
|
||||
* [Send an E2EE test notification to mobile device(s)](/api/e2ee-test-notify)
|
||||
* [Register E2EE push device](/api/register-push-device)
|
||||
* [Register E2EE push device to bouncer](/api/register-remote-push-device)
|
||||
|
||||
@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
|
||||
# https://zulip.readthedocs.io/en/latest/documentation/api.html#step-by-step-guide
|
||||
# Also available at docs/documentation/api.md.
|
||||
|
||||
API_FEATURE_LEVEL = 469
|
||||
API_FEATURE_LEVEL = 470
|
||||
|
||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
||||
# only when going from an old version of the code to a newer version. Bump
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
from zerver.lib.devices import check_device_id
|
||||
from zerver.models.devices import Device
|
||||
from zerver.models.users import UserProfile
|
||||
from zerver.tornado.django_api import send_event_on_commit
|
||||
@ -12,3 +13,15 @@ def do_register_device(user_profile: UserProfile) -> int:
|
||||
)
|
||||
send_event_on_commit(user_profile.realm, event, [user_profile.id])
|
||||
return device.id
|
||||
|
||||
|
||||
def do_remove_device(user_profile: UserProfile, device_id: int) -> None:
|
||||
device = check_device_id(device_id, user_profile.id)
|
||||
device.delete()
|
||||
|
||||
event = dict(
|
||||
type="device",
|
||||
op="remove",
|
||||
device_id=device_id,
|
||||
)
|
||||
send_event_on_commit(user_profile.realm, event, [user_profile.id])
|
||||
|
||||
@ -31,6 +31,7 @@ from zerver.lib.event_types import (
|
||||
EventDefaultStreams,
|
||||
EventDeleteMessage,
|
||||
EventDeviceAdd,
|
||||
EventDeviceRemove,
|
||||
EventDeviceUpdate,
|
||||
EventDirectMessage,
|
||||
EventDraftsAdd,
|
||||
@ -177,6 +178,7 @@ check_custom_profile_fields = make_checker(EventCustomProfileFields)
|
||||
check_default_stream_groups = make_checker(EventDefaultStreamGroups)
|
||||
check_default_streams = make_checker(EventDefaultStreams)
|
||||
check_device_add = make_checker(EventDeviceAdd)
|
||||
check_device_remove = make_checker(EventDeviceRemove)
|
||||
check_device_update = make_checker(EventDeviceUpdate)
|
||||
check_direct_message = make_checker(EventDirectMessage)
|
||||
check_draft_add = make_checker(EventDraftsAdd)
|
||||
|
||||
@ -296,6 +296,12 @@ class EventDeviceAdd(BaseEvent):
|
||||
device_id: int
|
||||
|
||||
|
||||
class EventDeviceRemove(BaseEvent):
|
||||
type: Literal["device"]
|
||||
op: Literal["remove"]
|
||||
device_id: int
|
||||
|
||||
|
||||
class EventDeviceUpdate(BaseEvent):
|
||||
type: Literal["device"]
|
||||
op: Literal["update"]
|
||||
|
||||
@ -2016,6 +2016,8 @@ def apply_event(
|
||||
"push_token_last_updated_timestamp": None,
|
||||
"push_registration_error_code": None,
|
||||
}
|
||||
elif event["op"] == "remove":
|
||||
del state["devices"][str(event["device_id"])]
|
||||
elif event["op"] == "update":
|
||||
if "push_key_id" in event:
|
||||
state["devices"][str(event["device_id"])]["push_key_id"] = event["push_key_id"]
|
||||
|
||||
@ -1737,6 +1737,21 @@ def register_device(client: Client) -> None:
|
||||
validate_against_openapi_schema(result, "/register_client_device", "post", "200")
|
||||
|
||||
|
||||
@openapi_test_function("/remove_client_device:post")
|
||||
def remove_device(client: Client) -> None:
|
||||
# First register a device to get a device_id.
|
||||
result = client.call_endpoint(url="/register_client_device", method="POST")
|
||||
device_id = result["device_id"]
|
||||
|
||||
# {code_example|start}
|
||||
# Remove a registered device.
|
||||
request = {"device_id": device_id}
|
||||
result = client.call_endpoint(url="/remove_client_device", method="POST", request=request)
|
||||
# {code_example|end}
|
||||
assert_success_response(result)
|
||||
validate_against_openapi_schema(result, "/remove_client_device", "post", "200")
|
||||
|
||||
|
||||
@openapi_test_function("/typing:post")
|
||||
def set_typing_status(client: Client) -> None:
|
||||
ensure_users([10, 11], ["hamlet", "iago"])
|
||||
@ -2066,6 +2081,7 @@ def test_users(client: Client, owner_client: Client) -> None:
|
||||
remove_fcm_token(client)
|
||||
register_push_device(client)
|
||||
register_device(client)
|
||||
remove_device(client)
|
||||
|
||||
|
||||
def test_streams(client: Client, nonadmin_client: Client) -> None:
|
||||
|
||||
@ -13184,6 +13184,36 @@ paths:
|
||||
description: |
|
||||
The ID of the newly registered device.
|
||||
example: {"msg": "", "device_id": 2, "result": "success"}
|
||||
/remove_client_device:
|
||||
post:
|
||||
operationId: remove-client-device
|
||||
summary: Remove a registered device
|
||||
tags: ["mobile"]
|
||||
description: |
|
||||
Mobile devices use this endpoint to remove their device record
|
||||
registered using [`POST /register_client_device`](/api/register-client-device)
|
||||
when the user logs out.
|
||||
|
||||
This endpoint is currently not useful for clients other than mobile.
|
||||
|
||||
**Changes**: New in Zulip 12.0 (feature level 470).
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
device_id:
|
||||
description: |
|
||||
The ID of the device to remove.
|
||||
type: integer
|
||||
example: 2
|
||||
required:
|
||||
- device_id
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/SimpleSuccess"
|
||||
/user_topics:
|
||||
post:
|
||||
operationId: update-user-topic
|
||||
|
||||
@ -14,3 +14,17 @@ class TestDeviceRegistration(ZulipTestCase):
|
||||
|
||||
device = Device.objects.get(id=data["device_id"])
|
||||
self.assertEqual(device.user_id, user.id)
|
||||
|
||||
def test_remove_device(self) -> None:
|
||||
user = self.example_user("hamlet")
|
||||
|
||||
self.assertEqual(Device.objects.count(), 0)
|
||||
|
||||
result = self.api_post(user, "/api/v1/register_client_device")
|
||||
data = self.assert_json_success(result)
|
||||
device = Device.objects.get(id=data["device_id"])
|
||||
|
||||
result = self.api_post(user, "/api/v1/remove_client_device", {"device_id": device.id})
|
||||
self.assert_json_success(result)
|
||||
|
||||
self.assertEqual(Device.objects.count(), 0)
|
||||
|
||||
@ -53,7 +53,7 @@ from zerver.actions.default_streams import (
|
||||
do_remove_streams_from_default_stream_group,
|
||||
lookup_default_stream_groups,
|
||||
)
|
||||
from zerver.actions.devices import do_register_device
|
||||
from zerver.actions.devices import do_register_device, do_remove_device
|
||||
from zerver.actions.invites import (
|
||||
do_create_multiuse_invite_link,
|
||||
do_invite_users,
|
||||
@ -177,6 +177,7 @@ from zerver.lib.event_schema import (
|
||||
check_default_streams,
|
||||
check_delete_message,
|
||||
check_device_add,
|
||||
check_device_remove,
|
||||
check_device_update,
|
||||
check_direct_message,
|
||||
check_draft_add,
|
||||
@ -4287,6 +4288,12 @@ class NormalActionsTest(BaseAction):
|
||||
do_register_device(self.user_profile)
|
||||
check_device_add("events[0]", events[0])
|
||||
|
||||
def test_remove_device(self) -> None:
|
||||
device = Device.objects.create(user=self.user_profile)
|
||||
with self.verify_action() as events:
|
||||
do_remove_device(self.user_profile, device.id)
|
||||
check_device_remove("events[0]", events[0])
|
||||
|
||||
def test_register_push_device(self) -> None:
|
||||
self.login_user(self.user_profile)
|
||||
device = Device.objects.create(user=self.user_profile)
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from pydantic import Json
|
||||
|
||||
from zerver.actions.devices import do_register_device
|
||||
from zerver.actions.devices import do_register_device, do_remove_device
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.typed_endpoint import typed_endpoint_without_parameters
|
||||
from zerver.lib.typed_endpoint import typed_endpoint, typed_endpoint_without_parameters
|
||||
from zerver.models.users import UserProfile
|
||||
|
||||
|
||||
@ -10,3 +11,14 @@ from zerver.models.users import UserProfile
|
||||
def register_device(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
|
||||
device_id = do_register_device(user_profile)
|
||||
return json_success(request, data={"device_id": device_id})
|
||||
|
||||
|
||||
@typed_endpoint
|
||||
def remove_device(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
*,
|
||||
device_id: Json[int],
|
||||
) -> HttpResponse:
|
||||
do_remove_device(user_profile, device_id)
|
||||
return json_success(request)
|
||||
|
||||
@ -61,7 +61,7 @@ from zerver.views.custom_profile_fields import (
|
||||
update_realm_custom_profile_field,
|
||||
update_user_custom_profile_data,
|
||||
)
|
||||
from zerver.views.devices import register_device
|
||||
from zerver.views.devices import register_device, remove_device
|
||||
from zerver.views.digest import digest_page
|
||||
from zerver.views.documentation import MarkdownDirectoryView, integrations_catalog, integrations_doc
|
||||
from zerver.views.drafts import create_drafts, delete_draft, edit_draft, fetch_drafts
|
||||
@ -612,6 +612,7 @@ v1_api_and_json_patterns = [
|
||||
rest_path("export/realm/<int:export_id>", DELETE=delete_realm_export),
|
||||
rest_path("export/realm/consents", GET=get_users_export_consents),
|
||||
rest_path("register_client_device", POST=register_device),
|
||||
rest_path("remove_client_device", POST=remove_device),
|
||||
]
|
||||
|
||||
# These views serve pages (HTML). As such, their internationalization
|
||||
|
||||
Loading…
Reference in New Issue
Block a user