From 3947b0c80a26b9126540ada996f027aac03063b7 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Bodas Date: Tue, 30 Mar 2021 16:21:54 +0530 Subject: [PATCH] linkifiers: Update API to send data using dictionaries. * This introduces a new event type `realm_linkifiers` and a new key for the initial data fetch of the same name. Newer clients will be expected to use these. * Backwards compatibility is ensured by changing neither the current event nor the /register key. The data which these hold is the same as before, but internally, it is generated by processing the `realm_linkifiers` data. We send both the old and the new event types to clients whenever the linkifiers are changed. Older clients will simply ignore the new event type, and vice versa. * The `realm/filters:GET` endpoint (which returns tuples) is currently used by none of the official Zulip clients. This commit replaces it with `realm/linkifiers:GET` which returns data in the new dictionary format. TODO: Update the `get_realm_filters` method in the API bindings, to hit this new URL instead of the old one. * This also updates the webapp frontend to use the newer events and keys. --- frontend_tests/node_tests/dispatch.js | 8 +- frontend_tests/node_tests/lib/events.js | 12 +- frontend_tests/node_tests/markdown.js | 54 +++++++-- static/js/markdown.js | 15 ++- static/js/server_events_dispatch.js | 8 +- static/js/settings_linkifiers.js | 21 ++-- static/js/ui_init.js | 2 +- templates/zerver/api/changelog.md | 17 +++ templates/zerver/api/get-linkifiers.md | 12 +- version.py | 2 +- zerver/lib/actions.py | 8 ++ zerver/lib/event_schema.py | 23 +++- zerver/lib/events.py | 7 ++ zerver/openapi/python_examples.py | 13 +- zerver/openapi/zulip.yaml | 154 ++++++++++++++++++++---- zerver/tests/test_event_system.py | 1 + zerver/tests/test_events.py | 14 ++- zerver/tests/test_home.py | 1 + zerver/tests/test_realm_linkifiers.py | 4 +- zerver/views/realm_linkifiers.py | 6 +- zproject/urls.py | 5 +- 21 files changed, 297 insertions(+), 90 deletions(-) diff --git a/frontend_tests/node_tests/dispatch.js b/frontend_tests/node_tests/dispatch.js index b4c189deec..e25e5540dd 100644 --- a/frontend_tests/node_tests/dispatch.js +++ b/frontend_tests/node_tests/dispatch.js @@ -516,13 +516,13 @@ run_test("realm_emoji", (override) => { } }); -run_test("linkifier", (override) => { - const event = event_fixtures.realm_filters; - page_params.realm_filters = []; +run_test("realm_linkifiers", (override) => { + const event = event_fixtures.realm_linkifiers; + page_params.realm_linkifiers = []; override(settings_linkifiers, "populate_linkifiers", noop); override(markdown, "update_linkifier_rules", noop); dispatch(event); - assert_same(page_params.realm_filters, event.realm_filters); + assert_same(page_params.realm_linkifiers, event.realm_linkifiers); }); run_test("realm_domains", (override) => { diff --git a/frontend_tests/node_tests/lib/events.js b/frontend_tests/node_tests/lib/events.js index bbaec5410a..d2f42121ff 100644 --- a/frontend_tests/node_tests/lib/events.js +++ b/frontend_tests/node_tests/lib/events.js @@ -456,9 +456,15 @@ exports.fixtures = { ], }, - realm_filters: { - type: "realm_filters", - realm_filters: [["#[123]", "ticket %(id)s", 55]], + realm_linkifiers: { + type: "realm_linkifiers", + realm_linkifiers: [ + { + pattern: "#[123]", + url_format: "ticket %(id)s", + id: 55, + }, + ], }, realm_user__add: { diff --git a/frontend_tests/node_tests/markdown.js b/frontend_tests/node_tests/markdown.js index ab8535c62f..80434ba65e 100644 --- a/frontend_tests/node_tests/markdown.js +++ b/frontend_tests/node_tests/markdown.js @@ -13,13 +13,22 @@ set_global("location", { origin: "http://zulip.zulipdev.com", }); -const example_realm_filters = [ - ["#(?P[0-9]{2,8})", "https://trac.example.com/ticket/%(id)s"], - ["ZBUG_(?P[0-9]{2,8})", "https://trac2.zulip.net/ticket/%(id)s"], - [ - "ZGROUP_(?P[0-9]{2,8}):(?P[0-9]{1,8})", - "https://zone_%(zone)s.zulip.net/ticket/%(id)s", - ], +const example_realm_linkifiers = [ + { + pattern: "#(?P[0-9]{2,8})", + url_format: "https://trac.example.com/ticket/%(id)s", + id: 1, + }, + { + pattern: "ZBUG_(?P[0-9]{2,8})", + url_format: "https://trac2.zulip.net/ticket/%(id)s", + id: 2, + }, + { + pattern: "ZGROUP_(?P[0-9]{2,8}):(?P[0-9]{1,8})", + url_format: "https://zone_%(zone)s.zulip.net/ticket/%(id)s", + id: 3, + }, ]; page_params.translate_emoticons = false; @@ -179,12 +188,12 @@ stream_data.add_sub(edgecase_stream_2); // streamTopicHandler and it would be parsed as edgecase_stream_2. stream_data.add_sub(amp_stream); -markdown.initialize(example_realm_filters, markdown_config.get_helpers()); +markdown.initialize(example_realm_linkifiers, markdown_config.get_helpers()); function test(label, f) { run_test(label, (override) => { page_params.realm_users = []; - markdown.update_linkifier_rules(example_realm_filters); + markdown.update_linkifier_rules(example_realm_linkifiers); f(override); }); } @@ -713,13 +722,28 @@ test("backend_only_linkifiers", () => { test("python_to_js_linkifier", () => { // The only way to reach python_to_js_linkifier is indirectly, hence the call // to update_linkifier_rules. - markdown.update_linkifier_rules([["/a(?im)a/g"], ["/a(?L)a/g"]]); + markdown.update_linkifier_rules([ + { + pattern: "/a(?im)a/g", + url_format: "http://example1.example.com", + id: 10, + }, + { + pattern: "/a(?L)a/g", + url_format: "http://example2.example.com", + id: 20, + }, + ]); let actual_value = marked.InlineLexer.rules.zulip.linkifiers; let expected_value = [/\/aa\/g(?!\w)/gim, /\/aa\/g(?!\w)/g]; assert.deepEqual(actual_value, expected_value); // Test case with multiple replacements. markdown.update_linkifier_rules([ - ["#cf(?P\\d+)(?P[A-Z][\\dA-Z]*)", "http://google.com"], + { + pattern: "#cf(?P\\d+)(?P[A-Z][\\dA-Z]*)", + url_format: "http://example3.example.com", + id: 30, + }, ]); actual_value = marked.InlineLexer.rules.zulip.linkifiers; expected_value = [/#cf(\d+)([A-Z][\dA-Z]*)(?!\w)/g]; @@ -729,7 +753,13 @@ test("python_to_js_linkifier", () => { "error", "python_to_js_linkifier: Invalid regular expression: /!@#@(!#&((!&(@#((?!\\w)/: Unterminated group", ); - markdown.update_linkifier_rules([["!@#@(!#&((!&(@#(", "http://google.com"]]); + markdown.update_linkifier_rules([ + { + pattern: "!@#@(!#&((!&(@#(", + url_format: "http://example4.example.com", + id: 40, + }, + ]); actual_value = marked.InlineLexer.rules.zulip.linkifiers; expected_value = []; assert.deepEqual(actual_value, expected_value); diff --git a/static/js/markdown.js b/static/js/markdown.js index 85693d71ac..da5a6b84aa 100644 --- a/static/js/markdown.js +++ b/static/js/markdown.js @@ -89,7 +89,7 @@ export function contains_backend_only_syntax(content) { // then don't render it locally. It is workaround for the fact that // javascript regex doesn't support lookbehind. const false_linkifier_match = linkifier_list.find((re) => { - const pattern = /[^\s"'(,:<]/.source + re[0].source + /(?!\w)/.source; + const pattern = /[^\s"'(,:<]/.source + re.pattern.source + /(?!\w)/.source; const regex = new RegExp(pattern); return regex.test(content); }); @@ -225,8 +225,8 @@ export function add_topic_links(message) { const links = []; for (const linkifier of linkifier_list) { - const pattern = linkifier[0]; - const url = linkifier[1]; + const pattern = linkifier.pattern; + const url = linkifier.url_format; let match; while ((match = pattern.exec(topic)) !== null) { let link_url = url; @@ -451,15 +451,18 @@ export function update_linkifier_rules(linkifiers) { const marked_rules = []; - for (const [pattern, url] of linkifiers) { - const [regex, final_url] = python_to_js_linkifier(pattern, url); + for (const linkifier of linkifiers) { + const [regex, final_url] = python_to_js_linkifier(linkifier.pattern, linkifier.url_format); if (!regex) { // Skip any linkifiers that could not be converted continue; } linkifier_map.set(regex, final_url); - linkifier_list.push([regex, final_url]); + linkifier_list.push({ + pattern: regex, + url_format: final_url, + }); marked_rules.push(regex); } diff --git a/static/js/server_events_dispatch.js b/static/js/server_events_dispatch.js index c5cd8f6050..32668e8566 100644 --- a/static/js/server_events_dispatch.js +++ b/static/js/server_events_dispatch.js @@ -342,10 +342,10 @@ export function dispatch_normal_event(event) { composebox_typeahead.update_emoji_data(); break; - case "realm_filters": - page_params.realm_filters = event.realm_filters; - markdown.update_linkifier_rules(page_params.realm_filters); - settings_linkifiers.populate_linkifiers(page_params.realm_filters); + case "realm_linkifiers": + page_params.realm_linkifiers = event.realm_linkifiers; + markdown.update_linkifier_rules(page_params.realm_linkifiers); + settings_linkifiers.populate_linkifiers(page_params.realm_linkifiers); break; case "realm_domains": diff --git a/static/js/settings_linkifiers.js b/static/js/settings_linkifiers.js index 1f40029498..efbb47e212 100644 --- a/static/js/settings_linkifiers.js +++ b/static/js/settings_linkifiers.js @@ -23,21 +23,21 @@ export function maybe_disable_widgets() { } } -function compare_by_index(a, b, i) { - if (a[i] > b[i]) { +function compare_values(x, y) { + if (x > y) { return 1; - } else if (a[i] === b[i]) { + } else if (x === y) { return 0; } return -1; } function sort_pattern(a, b) { - return compare_by_index(a, b, 0); + return compare_values(a.pattern, b.pattern); } function sort_url(a, b) { - return compare_by_index(a, b, 1); + return compare_values(a.url_format, b.url_format); } export function populate_linkifiers(linkifiers_data) { @@ -51,9 +51,9 @@ export function populate_linkifiers(linkifiers_data) { modifier(linkifier) { return render_admin_linkifier_list({ linkifier: { - pattern: linkifier[0], - url_format_string: linkifier[1], - id: linkifier[2], + pattern: linkifier.pattern, + url_format_string: linkifier.url_format, + id: linkifier.id, }, can_modify: page_params.is_admin, }); @@ -62,7 +62,8 @@ export function populate_linkifiers(linkifiers_data) { element: linkifiers_table.closest(".settings-section").find(".search"), predicate(item, value) { return ( - item[0].toLowerCase().includes(value) || item[1].toLowerCase().includes(value) + item.pattern.toLowerCase().includes(value) || + item.url_format.toLowerCase().includes(value) ); }, onupdate() { @@ -88,7 +89,7 @@ export function build_page() { meta.loaded = true; // Populate linkifiers table - populate_linkifiers(page_params.realm_filters); + populate_linkifiers(page_params.realm_linkifiers); $(".admin_linkifiers_table").on("click", ".delete", function (e) { e.preventDefault(); diff --git a/static/js/ui_init.js b/static/js/ui_init.js index 4d833bfdb2..b80e12ccb8 100644 --- a/static/js/ui_init.js +++ b/static/js/ui_init.js @@ -496,7 +496,7 @@ export function initialize_everything() { realm_emoji: emoji_params.realm_emoji, emoji_codes: generated_emoji_codes, }); - markdown.initialize(page_params.realm_filters, markdown_config.get_helpers()); + markdown.initialize(page_params.realm_linkifiers, markdown_config.get_helpers()); compose.initialize(); composebox_typeahead.initialize(); // Must happen after compose.initialize() search.initialize(); diff --git a/templates/zerver/api/changelog.md b/templates/zerver/api/changelog.md index e9b2e56d96..a4fe251942 100644 --- a/templates/zerver/api/changelog.md +++ b/templates/zerver/api/changelog.md @@ -10,6 +10,23 @@ below features are supported. ## Changes in Zulip 4.0 +**Feature level 54** + +* `GET /realm/filters` has been removed and replace with [`GET + /realm/linkifiers`](/api/get-linkifiers) which returns the data in a + cleaner dictionary format. +* [`GET /events`](/api/get-events): Introduced new event type + `realm_linkifiers`. The previous `realm_filters` event type is + still supported for backwards compatibility, but will be removed in + a future release. +* [`POST /register`](/api/register-queue): The response now supports a + `realm_linkifiers` event type, containing the same data as the + legacy `realm_filters` key, with a more extensible object + format. The previous `realm_filters` event type is still supported + for backwards compatibility, but will be removed in a future + release. The legacy `realm_filters` key is deprecated but remains + available for backwards compatibility. + **Feature level 53** * [`POST /register`](/api/register-queue): Added `max_topic_length` diff --git a/templates/zerver/api/get-linkifiers.md b/templates/zerver/api/get-linkifiers.md index db19d36326..f58694dcdb 100644 --- a/templates/zerver/api/get-linkifiers.md +++ b/templates/zerver/api/get-linkifiers.md @@ -1,32 +1,32 @@ # Get linkifiers -{generate_api_description(/realm/filters:get)} +{generate_api_description(/realm/linkifiers:get)} ## Usage examples {start_tabs} {tab|python} -{generate_code_example(python)|/realm/filters:get|example} +{generate_code_example(python)|/realm/linkifiers:get|example} {tab|curl} -{generate_code_example(curl)|/realm/filters:get|example} +{generate_code_example(curl)|/realm/linkifiers:get|example} {end_tabs} ## Parameters -{generate_api_arguments_table|zulip.yaml|/realm/filters:get} +{generate_api_arguments_table|zulip.yaml|/realm/linkifiers:get} ## Response #### Return values -{generate_return_values_table|zulip.yaml|/realm/filters:get} +{generate_return_values_table|zulip.yaml|/realm/linkifiers:get} #### Example response A typical successful JSON response may look like: -{generate_code_example|/realm/filters:get|fixture(200)} +{generate_code_example|/realm/linkifiers:get|fixture(200)} diff --git a/version.py b/version.py index a94337fff2..7c2cea12f6 100644 --- a/version.py +++ b/version.py @@ -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 = 53 +API_FEATURE_LEVEL = 54 # 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 diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py index 78ccecc7a8..971edc3ca9 100644 --- a/zerver/lib/actions.py +++ b/zerver/lib/actions.py @@ -236,6 +236,7 @@ from zerver.models import ( get_user_by_id_in_realm_including_cross_realm, get_user_profile_by_id, is_cross_realm_bot_email, + linkifiers_for_realm, query_for_ids, realm_filters_for_realm, validate_attachment_request, @@ -6593,6 +6594,13 @@ def do_mark_hotspot_as_read(user: UserProfile, hotspot: str) -> None: def notify_linkifiers(realm: Realm) -> None: + realm_linkifiers = linkifiers_for_realm(realm.id) + event = dict(type="realm_linkifiers", realm_linkifiers=realm_linkifiers) + send_event(realm, event, active_user_ids(realm.id)) + + # Below is code for backwards compatibility. The now deprecated + # "realm_filters" event-type is used by older clients, and uses + # tuples. realm_filters = realm_filters_for_realm(realm.id) event = dict(type="realm_filters", realm_filters=realm_filters) send_event(realm, event, active_user_ids(realm.id)) diff --git a/zerver/lib/event_schema.py b/zerver/lib/event_schema.py index 12476ad134..30227dbede 100644 --- a/zerver/lib/event_schema.py +++ b/zerver/lib/event_schema.py @@ -781,9 +781,26 @@ def check_realm_export( assert has_failed_timestamp == (export["failed_timestamp"] is not None) -# This type, like other instances of TupleType, is a legacy feature of -# a very old Zulip API; we plan to replace it with an object as those -# are more extensible. +realm_linkifier_type = DictType( + required_keys=[ + ("pattern", str), + ("url_format", str), + ("id", int), + ] +) + +realm_linkifiers_event = event_dict_type( + [ + ("type", Equals("realm_linkifiers")), + ("realm_linkifiers", ListType(realm_linkifier_type)), + ] +) +check_realm_linkifiers = make_checker(realm_linkifiers_event) + + +# This is a legacy event type to ensure backwards compatibility +# for old clients. Newer clients should handle only the +# "realm_linkifiers" event above. realm_filter_type = TupleType( [ # we should make this an object diff --git a/zerver/lib/events.py b/zerver/lib/events.py index 1a0891ea9c..f4d8c59eef 100644 --- a/zerver/lib/events.py +++ b/zerver/lib/events.py @@ -62,6 +62,7 @@ from zerver.models import ( get_default_stream_groups, get_realm_domains, get_realm_playgrounds, + linkifiers_for_realm, realm_filters_for_realm, ) from zerver.tornado.django_api import get_user_events, request_event_queue @@ -263,6 +264,10 @@ def fetch_initial_state_data( if want("realm_emoji"): state["realm_emoji"] = realm.get_emoji() + if want("realm_linkifiers"): + state["realm_linkifiers"] = linkifiers_for_realm(realm.id) + + # Backwards compatibility code. if want("realm_filters"): state["realm_filters"] = realm_filters_for_realm(realm.id) @@ -993,6 +998,8 @@ def apply_event( state["muted_users"] = event["muted_users"] elif event["type"] == "realm_filters": state["realm_filters"] = event["realm_filters"] + elif event["type"] == "realm_linkifiers": + state["realm_linkifiers"] = event["realm_linkifiers"] elif event["type"] == "realm_playgrounds": state["realm_playgrounds"] = event["realm_playgrounds"] elif event["type"] == "update_display_settings": diff --git a/zerver/openapi/python_examples.py b/zerver/openapi/python_examples.py index a370981d4a..17691ca27c 100644 --- a/zerver/openapi/python_examples.py +++ b/zerver/openapi/python_examples.py @@ -308,15 +308,18 @@ def get_subscription_status(client: Client) -> None: ) -@openapi_test_function("/realm/filters:get") -def get_realm_filters(client: Client) -> None: +@openapi_test_function("/realm/linkifiers:get") +def get_realm_linkifiers(client: Client) -> None: # {code_example|start} # Fetch all the filters in this organization - result = client.get_realm_filters() + result = client.call_endpoint( + url="/realm/linkifiers", + method="GET", + ) # {code_example|end} - validate_against_openapi_schema(result, "/realm/filters", "get", "200") + validate_against_openapi_schema(result, "/realm/linkifiers", "get", "200") @openapi_test_function("/realm/profile_fields:get") @@ -1459,7 +1462,7 @@ def test_queues(client: Client) -> None: def test_server_organizations(client: Client) -> None: - get_realm_filters(client) + get_realm_linkifiers(client) add_realm_filter(client) add_realm_playground(client) get_server_settings(client) diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml index e9f570a323..8536ea34eb 100644 --- a/zerver/openapi/zulip.yaml +++ b/zerver/openapi/zulip.yaml @@ -2377,6 +2377,69 @@ paths: Processing this event is important to doing Markdown local echo correctly. + + **Changes**: New in Zulip 4.0 (feature level 54), replacing the + previous `realm_filters` event type, which is still sent for + backwards compatibility reasons. + + Clients should migrate to requesting and processing the + `realm_linkifiers` event type when possible, since we plan to remove + the legacy `realm_filters` logic entirely in a future release. + properties: + id: + $ref: "#/components/schemas/EventIdSchema" + type: + allOf: + - $ref: "#/components/schemas/EventTypeSchema" + - enum: + - realm_linkifiers + realm_linkifiers: + type: array + description: | + Array of dictionaries where each dictionary contains details about + a single realm linkifier. + items: + type: object + additionalProperties: false + properties: + pattern: + type: string + description: | + The string regex pattern which represents the pattern that + should be linkified by this linkifier. + url_format: + type: string + description: | + The URL format string to be used for linkifying matches. + + id: + type: integer + description: | + The ID of the linkifier. + example: + { + "type": "realm_linkifiers", + "realm_linkifiers": + [ + { + "pattern": "#(?P[123])", + "url_format": "https://realm.com/my_realm_filter/%(id)s", + "id": 1, + }, + ], + "id": 0, + } + - type: object + additionalProperties: false + deprecated: true + description: | + Legacy event type. Sent to all users in a Zulip organization + when the set of configured [linkifiers](/help/add-a-custom-linkifier) + for the organization has changed. + + **Changes**: Deprecated in Zulip 4.0 (feature level 54), replaced by + the `realm_linkifiers` event type, which has a clearer name and format, + instead. properties: id: $ref: "#/components/schemas/EventIdSchema" @@ -6485,7 +6548,7 @@ paths: "msg": "Cannot deactivate the only organization owner", "result": "error", } - /realm/filters: + /realm/linkifiers: get: operationId: get_linkifiers tags: ["server_and_organizations"] @@ -6495,7 +6558,11 @@ paths: expression patterns that are automatically linkified when they appear in messages and topics. - `GET {{ api_url }}/v1/realm/filters` + `GET {{ api_url }}/v1/realm/linkifiers` + + **Changes**: New in Zulip 4.0 (feature level 54). On older versions, + a similar `GET /realm/filters` endpoint was available with each entry in + a `[pattern, url_format, id]` tuple format. responses: "200": description: Success. @@ -6508,35 +6575,41 @@ paths: properties: result: {} msg: {} - filters: + linkifiers: type: array - items: - type: array - items: - oneOf: - - type: string - - type: integer description: | - An array of tuples, each representing one of - the linkifiers set up in the - organization. Each of these tuples contain the - pattern, the formatted URL and the filter's - ID, in that order. See the [Create - linkifiers](/api/add-linkifier) article for - details on what each field means. + An array of objects, where each object describes a linkifier. + items: + type: object + additionalProperties: false + properties: + pattern: + type: string + description: | + The string regex pattern which represents the pattern that + should be linkified by this linkifier. + url_format: + type: string + description: | + The URL format string to be used for linkifying matches. + id: + type: integer + description: | + The ID of the linkifier. example: { "msg": "", - "filters": + "linkifiers": [ - [ - "#(?P[0-9]+)", - "https://github.com/zulip/zulip/issues/%(id)s", - 1, - ], + { + "pattern": "#(?P[0-9]+)", + "url_format": "https://github.com/zulip/zulip/issues/%(id)s", + "id": 1, + }, ], "result": "success", } + /realm/filters: post: operationId: add_linkifier tags: ["server_and_organizations"] @@ -7009,8 +7082,37 @@ paths: - type: array items: type: integer + realm_linkifiers: + type: array + description: | + Present if `realm_linkifiers` is present in `fetch_event_types`. + + Array of objects where each object describes a single + [linkifier](/help/add-a-custom-linkifier). + + **Changes**: New in Zulip 4.0 (feature level 54). Clients can + access these data on older server versions via the previous + `realm_filters` key. + items: + type: object + additionalProperties: false + properties: + pattern: + type: string + description: | + The string regex pattern which represents the pattern that + should be linkified on matching. + url_format: + type: string + description: | + The URL with which the pattern matching string should be linkified. + id: + type: integer + description: | + The ID of the linkifier. realm_filters: type: array + deprecated: true items: type: array items: @@ -7018,16 +7120,20 @@ paths: - type: integer - type: string description: | - Present if `realm_filters` is present in `fetch_event_types`. + Legacy property for linkifiers. Present if `realm_filters` is + present in `fetch_event_types`. An array of tuples (fixed-length arrays) where each tuple describes - a single realm filter ([linkifier](/help/add-a-custom-linkifier). + a single [linkifier](/help/add-a-custom-linkifier). The first element of the tuple is a string regex pattern which represents the pattern that should be linkified on matching. The second element is the URL with which the pattern matching string should be linkified with and the third element is the id of the realm filter. + + **Changes**: Deprecated in Zulip 4.0 (feature level 54), replaced by + the `realm_linkifiers` key instead. realm_playgrounds: type: array items: diff --git a/zerver/tests/test_event_system.py b/zerver/tests/test_event_system.py index e235a95aa7..26804c872f 100644 --- a/zerver/tests/test_event_system.py +++ b/zerver/tests/test_event_system.py @@ -871,6 +871,7 @@ class FetchQueriesTest(ZulipTestCase): realm_incoming_webhook_bots=0, realm_emoji=1, realm_filters=1, + realm_linkifiers=1, realm_playgrounds=1, realm_user=3, realm_user_groups=2, diff --git a/zerver/tests/test_events.py b/zerver/tests/test_events.py index 0fbe9d179f..b0e4f834e9 100644 --- a/zerver/tests/test_events.py +++ b/zerver/tests/test_events.py @@ -128,6 +128,7 @@ from zerver.lib.event_schema import ( check_realm_emoji_update, check_realm_export, check_realm_filters, + check_realm_linkifiers, check_realm_playgrounds, check_realm_update, check_realm_update_dict, @@ -1349,13 +1350,18 @@ class NormalActionsTest(BaseAction): regex = "#(?P[123])" url = "https://realm.com/my_realm_filter/%(id)s" - events = self.verify_action(lambda: do_add_linkifier(self.user_profile.realm, regex, url)) - check_realm_filters("events[0]", events[0]) + events = self.verify_action( + lambda: do_add_linkifier(self.user_profile.realm, 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, "#(?P[123])") + lambda: do_remove_linkifier(self.user_profile.realm, "#(?P[123])"), + num_events=2, ) - check_realm_filters("events[0]", events[0]) + 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( diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py index 925dfd040e..896f664cbe 100644 --- a/zerver/tests/test_home.py +++ b/zerver/tests/test_home.py @@ -167,6 +167,7 @@ class HomeTest(ZulipTestCase): "realm_invite_to_realm_policy", "realm_invite_to_stream_policy", "realm_is_zephyr_mirror_realm", + "realm_linkifiers", "realm_logo_source", "realm_logo_url", "realm_mandatory_topics", diff --git a/zerver/tests/test_realm_linkifiers.py b/zerver/tests/test_realm_linkifiers.py index 630e0fc26f..4d5e4b1c82 100644 --- a/zerver/tests/test_realm_linkifiers.py +++ b/zerver/tests/test_realm_linkifiers.py @@ -10,10 +10,10 @@ class RealmFilterTest(ZulipTestCase): self.login("iago") realm = get_realm("zulip") do_add_linkifier(realm, "#(?P[123])", "https://realm.com/my_realm_filter/%(id)s") - result = self.client_get("/json/realm/filters") + result = self.client_get("/json/realm/linkifiers") self.assert_json_success(result) self.assertEqual(200, result.status_code) - self.assertEqual(len(result.json()["filters"]), 1) + self.assertEqual(len(result.json()["linkifiers"]), 1) def test_create(self) -> None: self.login("iago") diff --git a/zerver/views/realm_linkifiers.py b/zerver/views/realm_linkifiers.py index d6a1f1a3ad..1c97ecb01c 100644 --- a/zerver/views/realm_linkifiers.py +++ b/zerver/views/realm_linkifiers.py @@ -6,13 +6,13 @@ from zerver.decorator import require_realm_admin from zerver.lib.actions import do_add_linkifier, do_remove_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, realm_filters_for_realm +from zerver.models import RealmFilter, UserProfile, linkifiers_for_realm # Custom realm linkifiers def list_linkifiers(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: - filters = realm_filters_for_realm(user_profile.realm_id) - return json_success({"filters": filters}) + linkifiers = linkifiers_for_realm(user_profile.realm_id) + return json_success({"linkifiers": linkifiers}) @require_realm_admin diff --git a/zproject/urls.py b/zproject/urls.py index cf66929cdb..6a18411315 100644 --- a/zproject/urls.py +++ b/zproject/urls.py @@ -262,8 +262,9 @@ v1_api_and_json_patterns = [ rest_path("realm/icon", POST=upload_icon, DELETE=delete_icon_backend, GET=get_icon_backend), # realm/logo -> zerver.views.realm_logo rest_path("realm/logo", POST=upload_logo, DELETE=delete_logo_backend, GET=get_logo_backend), - # realm/filters -> zerver.views.realm_linkifiers - rest_path("realm/filters", GET=list_linkifiers, POST=create_linkifier), + # 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/", DELETE=delete_linkifier), # realm/playgrounds -> zerver.views.realm_playgrounds rest_path("realm/playgrounds", POST=add_realm_playground),