mirror of https://github.com/zulip/zulip.git
linkifiers: Add an API to support the editing of linkifier.
This commit adds an API to `zproject/urls.py` to edit/update the realm linkifier. Its helper function to update the database is added in `zerver/lib/actions.py`. `zulip.yaml` is documented accordingly as well, clearly stating that this API updates one linkifier at a time. The tests are added for the API and helper function which updates the realm linkifier. Fixes #10830.
This commit is contained in:
parent
c180cd5fa1
commit
6509c4f8f4
|
@ -10,6 +10,11 @@ below features are supported.
|
|||
|
||||
## Changes in Zulip 4.0
|
||||
|
||||
**Feature level 57**
|
||||
|
||||
* [`PATCH /realm/filters/{filter_id}`](/api/update-linkifier): New
|
||||
endpoint added to update a realm linkifier.
|
||||
|
||||
**Feature level 56**
|
||||
|
||||
* [`POST /register`](/api/register-queue): Added a new setting
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# Update a linkifier
|
||||
|
||||
{generate_api_description(/realm/filters/{filter_id}:patch)}
|
||||
|
||||
## Usage examples
|
||||
|
||||
{start_tabs}
|
||||
{tab|python}
|
||||
|
||||
{generate_code_example(python)|/realm/filters/{filter_id}:patch|example}
|
||||
|
||||
{tab|curl}
|
||||
|
||||
{generate_code_example(curl)|/realm/filters/{filter_id}:patch|example}
|
||||
|
||||
{end_tabs}
|
||||
|
||||
## Parameters
|
||||
|
||||
{generate_api_arguments_table|zulip.yaml|/realm/filters/{filter_id}:patch}
|
||||
|
||||
## Response
|
||||
|
||||
#### Return values
|
||||
|
||||
{generate_return_values_table|zulip.yaml|/realm/filters/{filter_id}:patch}
|
||||
|
||||
#### Example response
|
||||
|
||||
A typical successful JSON response may look like:
|
||||
|
||||
{generate_code_example|/realm/filters/{filter_id}:patch|fixture(200)}
|
|
@ -59,6 +59,7 @@
|
|||
* [Get server settings](/api/get-server-settings)
|
||||
* [Get linkifiers](/api/get-linkifiers)
|
||||
* [Add a linkifier](/api/add-linkifier)
|
||||
* [Update a linkifier](/api/update-linkifier)
|
||||
* [Remove a linkifier](/api/remove-linkifier)
|
||||
* [Add a playground](/api/add-playground)
|
||||
* [Remove a playground](/api/remove-playground)
|
||||
|
|
|
@ -30,7 +30,7 @@ DESKTOP_WARNING_VERSION = "5.2.0"
|
|||
#
|
||||
# Changes should be accompanied by documentation explaining what the
|
||||
# new level means in templates/zerver/api/changelog.md.
|
||||
API_FEATURE_LEVEL = 56
|
||||
API_FEATURE_LEVEL = 57
|
||||
|
||||
# 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
|
||||
|
|
|
@ -6647,6 +6647,17 @@ def do_remove_linkifier(
|
|||
notify_linkifiers(realm)
|
||||
|
||||
|
||||
def do_update_linkifier(realm: Realm, id: int, pattern: str, url_format_string: str) -> None:
|
||||
pattern = pattern.strip()
|
||||
url_format_string = url_format_string.strip()
|
||||
linkifier = RealmFilter.objects.get(realm=realm, id=id)
|
||||
linkifier.pattern = pattern
|
||||
linkifier.url_format_string = url_format_string
|
||||
linkifier.full_clean()
|
||||
linkifier.save(update_fields=["pattern", "url_format_string"])
|
||||
notify_linkifiers(realm)
|
||||
|
||||
|
||||
def get_emails_from_user_ids(user_ids: Sequence[int]) -> Dict[int, str]:
|
||||
# We may eventually use memcached to speed this up, but the DB is fast.
|
||||
return UserProfile.emails_from_ids(user_ids)
|
||||
|
|
|
@ -372,6 +372,25 @@ def add_realm_filter(client: Client) -> None:
|
|||
validate_against_openapi_schema(result, "/realm/filters", "post", "200")
|
||||
|
||||
|
||||
@openapi_test_function("/realm/filters/{filter_id}:patch")
|
||||
def update_realm_filter(client: Client) -> None:
|
||||
|
||||
# {code_example|start}
|
||||
# Update the linkifier (realm_filter) with ID 1
|
||||
filter_id = 1
|
||||
request = {
|
||||
"pattern": "#(?P<id>[0-9]+)",
|
||||
"url_format_string": "https://github.com/zulip/zulip/issues/%(id)s",
|
||||
}
|
||||
|
||||
result = client.call_endpoint(
|
||||
url=f"/realm/filters/{filter_id}", method="PATCH", request=request
|
||||
)
|
||||
# {code_example|end}
|
||||
|
||||
validate_against_openapi_schema(result, "/realm/filters/{filter_id}", "patch", "200")
|
||||
|
||||
|
||||
@openapi_test_function("/realm/filters/{filter_id}:delete")
|
||||
def remove_realm_filter(client: Client) -> None:
|
||||
|
||||
|
@ -1464,6 +1483,7 @@ def test_server_organizations(client: Client) -> None:
|
|||
|
||||
get_realm_linkifiers(client)
|
||||
add_realm_filter(client)
|
||||
update_realm_filter(client)
|
||||
add_realm_playground(client)
|
||||
get_server_settings(client)
|
||||
remove_realm_filter(client)
|
||||
|
|
|
@ -6682,6 +6682,35 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/JsonSuccess"
|
||||
patch:
|
||||
operationId: update_linkifier
|
||||
tags: ["server_and_organizations"]
|
||||
description: |
|
||||
Update a [linkifier](/help/add-a-custom-linkifier), regular
|
||||
expression patterns that are automatically linkified when they appear
|
||||
in messages and topics.
|
||||
|
||||
`PATCH {{ api_url }}/v1/realm/filters/{filter_id}`
|
||||
|
||||
**Changes**: New in Zulip 4.0 (feature level 57).
|
||||
parameters:
|
||||
- name: filter_id
|
||||
in: path
|
||||
description: |
|
||||
The ID of the linkifier that you want to update.
|
||||
schema:
|
||||
type: integer
|
||||
example: 2
|
||||
required: true
|
||||
- $ref: "#/components/parameters/LinkifierPattern"
|
||||
- $ref: "#/components/parameters/LinkifierURLFormatString"
|
||||
responses:
|
||||
"200":
|
||||
description: Success.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/JsonSuccess"
|
||||
/realm/playgrounds:
|
||||
post:
|
||||
operationId: add_realm_playground
|
||||
|
|
|
@ -86,6 +86,7 @@ from zerver.lib.actions import (
|
|||
do_unmute_topic,
|
||||
do_unmute_user,
|
||||
do_update_embedded_data,
|
||||
do_update_linkifier,
|
||||
do_update_message,
|
||||
do_update_message_flags,
|
||||
do_update_outgoing_webhook_service,
|
||||
|
@ -1356,13 +1357,21 @@ class NormalActionsTest(BaseAction):
|
|||
check_realm_linkifiers("events[0]", events[0])
|
||||
check_realm_filters("events[1]", events[1])
|
||||
|
||||
regex = "#(?P<id>[0-9]+)"
|
||||
linkifier_id = events[0]["realm_linkifiers"][0]["id"]
|
||||
events = self.verify_action(
|
||||
lambda: do_remove_linkifier(self.user_profile.realm, "#(?P<id>[123])"),
|
||||
lambda: do_update_linkifier(self.user_profile.realm, linkifier_id, regex, url),
|
||||
num_events=2,
|
||||
)
|
||||
check_realm_linkifiers("events[0]", events[0])
|
||||
check_realm_filters("events[1]", events[1])
|
||||
|
||||
events = self.verify_action(
|
||||
lambda: do_remove_linkifier(self.user_profile.realm, regex), num_events=2
|
||||
)
|
||||
check_realm_linkifiers("events[0]", events[0])
|
||||
check_realm_filters("events[1]", events[1])
|
||||
|
||||
def test_realm_domain_events(self) -> None:
|
||||
events = self.verify_action(
|
||||
lambda: do_add_realm_domain(self.user_profile.realm, "zulip.org", False)
|
||||
|
|
|
@ -124,3 +124,50 @@ class RealmFilterTest(ZulipTestCase):
|
|||
result = self.client_delete(f"/json/realm/filters/{linkifier_id}")
|
||||
self.assert_json_success(result)
|
||||
self.assertEqual(RealmFilter.objects.count(), linkifiers_count - 1)
|
||||
|
||||
def test_update(self) -> None:
|
||||
self.login("iago")
|
||||
data = {
|
||||
"pattern": "#(?P<id>[123])",
|
||||
"url_format_string": "https://realm.com/my_realm_filter/%(id)s",
|
||||
}
|
||||
result = self.client_post("/json/realm/filters", info=data)
|
||||
self.assert_json_success(result)
|
||||
|
||||
linkifier_id = result.json()["id"]
|
||||
data = {
|
||||
"pattern": "#(?P<id>[0-9]+)",
|
||||
"url_format_string": "https://realm.com/my_realm_filter/issues/%(id)s",
|
||||
}
|
||||
result = self.client_patch(f"/json/realm/filters/{linkifier_id}", info=data)
|
||||
self.assert_json_success(result)
|
||||
self.assertIsNotNone(re.match(data["pattern"], "#1234"))
|
||||
|
||||
# Verify that the linkifier is updated accordingly.
|
||||
result = self.client_get("/json/realm/linkifiers")
|
||||
self.assert_json_success(result)
|
||||
linkifier = result.json()["linkifiers"]
|
||||
self.assertEqual(len(linkifier), 1)
|
||||
self.assertEqual(linkifier[0]["pattern"], "#(?P<id>[0-9]+)")
|
||||
self.assertEqual(
|
||||
linkifier[0]["url_format"], "https://realm.com/my_realm_filter/issues/%(id)s"
|
||||
)
|
||||
|
||||
data = {
|
||||
"pattern": r"ZUL-(?P<id>\d++)",
|
||||
"url_format_string": "https://realm.com/my_realm_filter/%(id)s",
|
||||
}
|
||||
result = self.client_patch(f"/json/realm/filters/{linkifier_id}", info=data)
|
||||
self.assert_json_error(
|
||||
result, "Invalid linkifier pattern. Valid characters are [ a-zA-Z_#=/:+!-]."
|
||||
)
|
||||
|
||||
data["pattern"] = r"ZUL-(?P<id>\d+)"
|
||||
data["url_format_string"] = "$fgfg"
|
||||
result = self.client_patch(f"/json/realm/filters/{linkifier_id}", info=data)
|
||||
self.assert_json_error(result, "Enter a valid URL.")
|
||||
|
||||
data["pattern"] = r"#(?P<id>[123])"
|
||||
data["url_format_string"] = "https://realm.com/my_realm_filter/%(id)s"
|
||||
result = self.client_patch(f"/json/realm/filters/{linkifier_id + 1}", info=data)
|
||||
self.assert_json_error(result, "Linkifier not found.")
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.http import HttpRequest, HttpResponse
|
|||
from django.utils.translation import gettext as _
|
||||
|
||||
from zerver.decorator import require_realm_admin
|
||||
from zerver.lib.actions import do_add_linkifier, do_remove_linkifier
|
||||
from zerver.lib.actions import do_add_linkifier, do_remove_linkifier, do_update_linkifier
|
||||
from zerver.lib.request import REQ, has_request_variables
|
||||
from zerver.lib.response import json_error, json_success
|
||||
from zerver.models import RealmFilter, UserProfile, linkifiers_for_realm
|
||||
|
@ -43,3 +43,26 @@ def delete_linkifier(
|
|||
except RealmFilter.DoesNotExist:
|
||||
return json_error(_("Linkifier not found."))
|
||||
return json_success()
|
||||
|
||||
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def update_linkifier(
|
||||
request: HttpRequest,
|
||||
user_profile: UserProfile,
|
||||
filter_id: int,
|
||||
pattern: str = REQ(),
|
||||
url_format_string: str = REQ(),
|
||||
) -> HttpResponse:
|
||||
try:
|
||||
do_update_linkifier(
|
||||
realm=user_profile.realm,
|
||||
id=filter_id,
|
||||
pattern=pattern,
|
||||
url_format_string=url_format_string,
|
||||
)
|
||||
return json_success()
|
||||
except RealmFilter.DoesNotExist:
|
||||
return json_error(_("Linkifier not found."))
|
||||
except ValidationError as e:
|
||||
return json_error(e.messages[0], data={"errors": dict(e)})
|
||||
|
|
|
@ -118,7 +118,12 @@ from zerver.views.realm_domains import (
|
|||
from zerver.views.realm_emoji import delete_emoji, list_emoji, upload_emoji
|
||||
from zerver.views.realm_export import delete_realm_export, export_realm, get_realm_exports
|
||||
from zerver.views.realm_icon import delete_icon_backend, get_icon_backend, upload_icon
|
||||
from zerver.views.realm_linkifiers import create_linkifier, delete_linkifier, list_linkifiers
|
||||
from zerver.views.realm_linkifiers import (
|
||||
create_linkifier,
|
||||
delete_linkifier,
|
||||
list_linkifiers,
|
||||
update_linkifier,
|
||||
)
|
||||
from zerver.views.realm_logo import delete_logo_backend, get_logo_backend, upload_logo
|
||||
from zerver.views.realm_playgrounds import add_realm_playground, delete_realm_playground
|
||||
from zerver.views.registration import (
|
||||
|
@ -265,7 +270,7 @@ v1_api_and_json_patterns = [
|
|||
# realm/filters and realm/linkifiers -> zerver.views.realm_linkifiers
|
||||
rest_path("realm/linkifiers", GET=list_linkifiers),
|
||||
rest_path("realm/filters", POST=create_linkifier),
|
||||
rest_path("realm/filters/<int:filter_id>", DELETE=delete_linkifier),
|
||||
rest_path("realm/filters/<int:filter_id>", DELETE=delete_linkifier, PATCH=update_linkifier),
|
||||
# realm/playgrounds -> zerver.views.realm_playgrounds
|
||||
rest_path("realm/playgrounds", POST=add_realm_playground),
|
||||
rest_path("realm/playgrounds/<int:playground_id>", DELETE=delete_realm_playground),
|
||||
|
|
Loading…
Reference in New Issue