Commit Graph

8472 Commits

Author SHA1 Message Date
Alex Vandiver
e0d3176098 digest: Increase size of stream cache.
Since the cache is flushed when the cutoff or realm changes, the
maximum size of the cache should cap out at the number of streams in
the realm.  Raise the max cache size, now that this will not simply
lead to useless cache space for smaller servers.
2023-09-13 13:25:59 -07:00
Alex Vandiver
a8a1f10f3c digest: Clear the cache once we move to a new realm / cutoff value. 2023-09-13 13:25:59 -07:00
Alex Vandiver
39358f77dd digest: Enqueue emails as we generate the contexts.
There is now no longer any reason to have the scheduled_email
enqueuing wait until all of the users' contexts have been generated.
Switch to returning the contexts as an iterator, and send them as we
compute them.
2023-09-13 13:25:59 -07:00
Alex Vandiver
b9f72bdd68 digest: Switch loop to early-abort for clarity. 2023-09-13 13:25:59 -07:00
Alex Vandiver
b555d3f553 digest: Cache per-stream recent topics, rather than batching.
The query plan for fetching recent messages from the arbitrary set of
streams formed by the intersection of 30 random users can be quite
bad, and can descend into a sequential scan on `zerver_recipient`.
Worse, this work of pulling recent messages out is redone if the
stream appears in the next batch of 30 users.

Instead, pull the recent messages for a stream on a one-by-one basis,
but cache them in an in-memory cache.  Since digests are enqueued in
30-user batches but still one-realm-at-a-time, work will be saved both
in terms of faster query plans whose results can also be reused across
batches.

This requires that we pull the stream-id to stream-name mapping for
_all_ streams in the realm at once, but that is well-indexed and
unlikely to cause performance issues -- in fact, it may be faster
than pulling a random subset of the streams in the realm.
2023-09-13 13:25:59 -07:00
Alex Vandiver
ffb6c95bba email_notifications: Make stream_id_map optional to build_message_list.
This feels cleaner than passing an empty dict.
2023-09-13 13:25:59 -07:00
Alex Vandiver
f8a9779b54 digest: Rename get_slim_stream_map slightly and explain its name more. 2023-09-13 13:25:59 -07:00
Alex Vandiver
bca9821c89 digest: Rename get_recent_streams for clarity. 2023-09-13 13:25:59 -07:00
Alex Vandiver
524d4913b3 digest: Filter out users who have joined recently in SQL. 2023-09-13 13:25:59 -07:00
Alex Vandiver
d8668ab242 digest: Narrow the query by only fetching the sender full name. 2023-09-13 13:25:59 -07:00
Alex Vandiver
058a168bfe digest: Rewrite target-user algorithm as one query.
There is no reason to do this set manipulation in Python.
2023-09-13 13:25:59 -07:00
Alex Vandiver
584c202d36 digest: Remove unnecessary should_process_digest function. 2023-09-13 13:25:59 -07:00
David Rosa
6505583b7e help: Update user management pages.
- Documents how to access the "Manage user" tab via a user's profile.
2023-09-13 13:06:04 -07:00
Anders Kaseorg
2665a3ce2b python: Elide unnecessary list wrappers.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-09-13 12:41:23 -07:00
Anders Kaseorg
8efa418205 typed_endpoint: Add missing tuple comma for OptionalTopic aliases.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-09-12 11:49:11 -07:00
Anders Kaseorg
6988622fe8 ruff: Enable B023 Function definition does not bind loop variable.
Python’s loop scoping is misdesigned, resulting in a very common
gotcha for functions that close over loop variables [1].  The general
problem is so bad that even the Go developers plan to break
compatibility in order to fix the same design mistake in their
language [2].

Enable the Ruff rule function-uses-loop-variable (B023) [3], which
conservatively prohibits functions from binding loop variables at all.

[1] https://docs.python-guide.org/writing/gotchas/#late-binding-closures
[2] https://go.dev/s/loopvar-design
[3] https://beta.ruff.rs/docs/rules/function-uses-loop-variable/

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-09-11 18:03:45 -07:00
Anders Kaseorg
cf4791264c python: Replace functools.partial with type-safe returns.curry.partial.
The type annotation for functools.partial uses unchecked Any for all
the function parameters (both early and late).  returns.curry.partial
uses a mypy plugin to check the parameters safely.

https://returns.readthedocs.io/en/latest/pages/curry.html

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-09-11 18:03:45 -07:00
Alex Vandiver
b94402152d models: Always search Messages with a realm_id or id limit.
Unless there is a limit on `id`, always provide a `realm_id` limit as
well.  We also notate which index is expected to be used in each
query.
2023-09-11 15:00:37 -07:00
Alex Vandiver
f9dd2549eb narrow: Set a realm_id limit on messages in user searches. 2023-09-11 15:00:37 -07:00
Alex Vandiver
067de6f948 coverage: Skip zerver.lib.migrate coverage.
It is only covered when we run migration tests, which we are not
guaranteed to always be able to do.
2023-09-11 15:00:37 -07:00
Alex Vandiver
d6745209f2 django: Use .exists() instead of .count() when possible. 2023-09-11 15:00:37 -07:00
Alex Vandiver
9d3d57e786 message_send: Inline single use of filter_by_exact_message_topic.
Matching the topic exactly, as opposed to case-insensitively, is not a
common operation, and one that we want to make difficult to do
accidentally.  Inline the single use case of it.
2023-09-11 15:00:37 -07:00
Alex Vandiver
5a0f4a1a22 messages: Limit to "id" column for max-message-id computation.
This lets PostgreSQL use an "Index Only Scan" which is slightly faster
than an "Index scan".
2023-09-11 15:00:37 -07:00
Alex Vandiver
631868a05b users: Refactor and optimize max_message_id_for_user by removing a join.
This algorithm existed in multiple places, with different queries.
Since we only access properties in the UserMessage table, we
standardize on the much simpler and faster Index Only Scan, rather
than a merge join.
2023-09-11 15:00:37 -07:00
Anders Kaseorg
1905df2342 requirements: Upgrade Python requirements.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-09-09 12:53:39 -07:00
Adrián Oliva
732ad89f3d markdown: Fix URL link topic skipping query.
When searching for links inside a topic name, the question mark (?)
was used to split the topic. If a URL had a query after the URL
(e.g., "?foo=bar"), then the query was trimmed from the URL.

Removing the question mark from `basic_link_splitter` is sufficient
to fix this issue. The `get_web_link_regex` function then removes
the trailing punctuation if any, including literal question marks.

Fixes #26368.
2023-09-08 16:17:11 -07:00
N-Shar-ma
8c91c91d86 widgets: Fix bug where a new line right after /todo broke rendering.
When there was no space right after `/todo` but there was content on a
new line, the message would be rendered plainly, not as a todo widget.
This was because we split on only the space character to then check if
the first token was a valid widget.

Now we split on both spaces and newlines to extract the widget name,
irrespective of whether it is followed by a space or a newline. This
results in the message being rendered as a todo widget as expected.
2023-09-08 15:39:07 -07:00
Lauryn Menard
11adc0f37d demo-organizations: Rename shortend versions of 'demo organization'.
Rename existing shortened references to demo organizations, like
`is_demo_org` or `demo-org-warning`, that have been used in the
codebase so far and replace them to be like the `models.py`
variable: `Realm.demo_organization_scheduled_deletion_date`.
2023-09-08 15:17:23 -07:00
Alex Vandiver
61262c7b9a tabbed_sections: Fix a backtrack-able regex.
This REDOS was not exploitable, as its content is only read from
checked-in files; regardless, simplify it to not backtrack.  We also
do not actually have any location which use leading or trailing
whitespace, so remove those optional bits.
2023-09-08 14:51:51 -07:00
Zixuan James Li
574740dda4 webhooks: Migrate check_send_webhook_message to use @typed_endpoint.
This function is used by almost all webhooks.

To support it, we use the "api_ignore_parameter" flag so that positional
arguments like topic and body that are not intended to be parsed from
the request can be ignored.
2023-09-08 08:20:17 -07:00
Zixuan James Li
910f69465c drafts: Migrate drafts to use @typed_endpoint.
This demonstrates the use of BaseModel to replace a check_dict_only
validator.

We also add support to referring to $defs in the OpenAPI tests. In the
future, we can descend down each object instead of mapping them to dict
for more accurate checks.
2023-09-08 08:20:17 -07:00
Zixuan James Li
6201914fd3 message_edit: Migrate message_edit to use @typed_endpoint.
This demonstrates how an alias is created and its suitable use case, the
use of PathOnly, NonNegativeInt, and Literal.
2023-09-08 08:20:17 -07:00
Zixuan James Li
c336bf0398 api: Avoid programming errors due to nested Annotated types.
We want to reject ambiguous type annotations that set ApiParamConfig
inside a Union. If a parameter is Optional and has a default of None, we
prefer Annotated[Optional[T], ...] over Optional[Annotated[T, ...]].

This implements a check that detects Optional[Annotated[T, ...]] and
raise an assertion error if ApiParamConfig is in the annotation. It also
checks if the type annotation contains any ApiParamConfig objects that
are ignored, which can happen if the Annotated type is nested inside
another type like List, Union, etc.

Note that because
param: Annotated[Optional[T], ...] = None
and
param: Optional[Annotated[Optional[T], ...]] = None
are equivalent in runtime prior to Python 3.11, there is no way for us
to distinguish the two. So we cannot detect that in runtime.
See also: https://github.com/python/cpython/issues/90353
2023-09-08 08:20:17 -07:00
Zixuan James Li
5a7b1065e5 api: Rewrite argument type test for clarity.
We refactor HostRequestMock so that it now proper populates the request
body given the post data, assuming that the request is JSON encoded.
2023-09-08 08:20:17 -07:00
Zixuan James Li
f4caf9dd79 api: Add new typed_endpoint decorators.
The goal of typed_endpoint is to replicate most features supported by
has_request_variables, and to improve on top of it. There are some
unresolved issues that we don't plan to work on currently. For example,
typed_endpoint does not support ignored_parameters_supported for 400
responses, and it does not run validators on path-only arguments.

Unlike has_request_variables, typed_endpoint supports error handling by
processing validation errors from Pydantic.

Most features supported by has_request_variables are supported by
typed_endpoint in various ways.

To define a function, use a syntax like this with Annotated if there is
any metadata you want to associate with a parameter, do note that
parameters that are not keyword-only are ignored from the request:
```
@typed_endpoint
def view(
    request: HttpRequest,
    user_profile: UserProfile,
    *,
    foo: Annotated[int, ApiParamConfig(path_only=True)],
    bar: Json[int],
    other: Annotated[
        Json[int],
        ApiParamConfig(
            whence="lorem",
            documentation_status=NTENTIONALLY_UNDOCUMENTED
        )
    ] = 10,
) -> HttpResponse:
    ....
```

There are also some shorthands for the commonly used annotated types,
which are encouraged when applicable for better readability and less
typing:
```
WebhookPayload = Annotated[Json[T], ApiParamConfig(argument_type_is_body=True)]
PathOnly = Annotated[T, ApiParamConfig(path_only=True)]
```

Then the view function above can be rewritten as:
```
@typed_endpoint
def view(
    request: HttpRequest,
    user_profile: UserProfile,
    *,
    foo: PathOnly[int],
    bar: Json[int],
    other: Annotated[
        Json[int],
        ApiParamConfig(
            whence="lorem",
            documentation_status=INTENTIONALLY_UNDOCUMENTED
        )
    ] = 10,
) -> HttpResponse:
    ....
```

There are some intentional restrictions:
- A single parameter cannot have more than one ApiParamConfig
- Path-only parameters cannot have default values
- argument_type_is_body is incompatible with whence
- Arguments of name "request", "user_profile", "args", and "kwargs" and
  etc. are ignored by typed_endpoint.
- positional-only arguments are not supported by typed_endpoint. Only
  keyword-only parameters are expected to be parsed from the request.
- Pydantic's strict mode is always enabled, because we don't want to
  coerce input parsed from JSON into other types unnecessarily.
- Using strict mode all the time also means that we should always use
  Json[int] instead of int, because it is only possible for the request
  to have data of type str, and a type annotation of int will always
  reject such data.

typed_endpoint's handling of ignored_parameters_unsupported is mostly
identical to that of has_request_variables.
2023-09-08 08:20:17 -07:00
Anders Kaseorg
0ce6dcb905 mypy: Upgrade mypy from 1.4.1 to 1.5.1.
_default_manager is the same as objects on most of our models. But
when a model class is stored in a variable, the type system doesn’t
know which model the variable is referring to, so it can’t know that
objects even exists (Django doesn’t add it if the user added a custom
manager of a different name). django-stubs used to incorrectly assume
it exists unconditionally, but it no longer does.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-09-07 17:51:42 -07:00
Tim Abbott
6c83bbcbdb settings: Disallow everyone group for new setting.
This is important because the "guests" value isn't one that we'd
expect anyone to pick intentionally, and in particular isn't an
available option for the similar/adjacent "email invitations" setting.
2023-09-07 14:21:01 -07:00
Ujjawal Modi
ec49c3acc8 invites: Rename can_invite_others_to_realm local variables.
This commit rename the existing setting `Who can invite users to this
organization` to `Who can send email invitations to new users` and
also renames all the variables related to this setting that do not
require a change to the API.

This was done for better code readability as a new setting
`Who can create invite links` will be added in future commits.
2023-09-07 14:21:01 -07:00
Ujjawal Modi
f67cef8885 invite: Add new setting for "Who can create multiuse invite links".
This commit does the backend changes required for adding a realm
setting based on groups permission model and does the API changes
required for the new setting `Who can create multiuse invite link`.
2023-09-07 14:21:01 -07:00
Ujjawal Modi
9eccb4336e types: Add id_field_name field to GroupPermissionSetting type.
This commit adds id_field_name field to GroupPermissionSetting
type which will be used to store the string formed by concatenation
of setting_name and `_id`.
2023-09-07 14:21:01 -07:00
Ujjawal Modi
72b099524d internal_realm: Single transaction for changes while creating realm.
This commit makes the database changes while creating internal_realm
to be done in a single transaction.
This is needed for deferring the foreign key constraints
to the end of transaction.
2023-09-07 14:21:01 -07:00
Anders Kaseorg
81bd63cb46 ruff: Fix PIE808 Unnecessary start argument in range.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-09-01 14:57:01 -07:00
Prakhar Pratyush
5d8897b909 push_notifications: Remove 'alert' field from the payload for android.
This commit removes the 'alert' field from the payload for
Android via GCM/FCM.

The alert strings generated do not get used at all and have
not been used since at least 2019. On Android, we construct
the notification UI ourselves in the client, and we ignore
the alert string.
2023-09-01 10:46:16 -07:00
Eeshan Garg
5e33ae8adf demo-orgs: Create dev environment demo organization without email.
To make creation of demo organizations feel lightweight for users,
we do not want to require an email address at sign-up. Instead an
empty string will used for the new realm owner's email. Currently
implements that for new demo organizations in the development
environment.

Because the user's email address does not exist, we don't enqueue
any of the welcome emails upon account/realm creation, and we
don't create/send new login emails.

This is a part of #19523.

Co-authored by: Tim Abbott <tabbott@zulip.com>
Co-authored by: Lauryn Menard <lauryn@zulip.com>
2023-08-31 15:02:16 -07:00
David Rosa
4b8c99b01a widgets: Rename confusing attribute name in tabbed_sections.py.
Renames misleading attribute in HTML template using `code-section`
to refer to both language toggles in API docs and app toggles in
help center docs.
2023-08-31 11:55:28 -07:00
Satyam Bansal
d8998ab040 events: Add display name and event types to realm_incoming_webhook_bots. 2023-08-30 15:54:13 -07:00
Satyam Bansal
2370372705 integrations: Extract integration event types returning function. 2023-08-30 15:54:13 -07:00
Anders Kaseorg
792a44b382 push_notifications: Fix logging.exception misuse.
logging.exception should only be called from an exception handler.
https://docs.python.org/3/library/logging.html#logging.exception

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-08-30 12:45:45 -07:00
Alex Vandiver
7787fe3f49 push_notifications: Send all APNS devices in parallel.
Instead of starting up one event loop for every device send, use
asyncio.gather to send to all of a user's devices at once.
2023-08-30 11:56:52 -07:00
Alex Vandiver
69825cd54c push_notifications: Drop error messages from failure to send.
We handle, and possibly log, these errors ourselves.
2023-08-30 11:56:52 -07:00