api_documentation: Add "/invites" endpoint to the api documentation.

This commit is contained in:
Vector73 2024-04-01 20:08:09 +05:30 committed by Tim Abbott
parent e217d35ccd
commit 7a55bb218b
5 changed files with 375 additions and 19 deletions

View File

@ -378,10 +378,11 @@ No changes; feature level used for Zulip 8.0 release.
to create reusable invitation links. Previously, this endpoint was to create reusable invitation links. Previously, this endpoint was
restricted to admin users only. restricted to admin users only.
* `GET /invites`: Endpoint response for non-admin users now includes both * [`GET /invites`](/api/get-invites): Endpoint response for non-admin users now
email invitations and reusable invitation links that they have created. includes both email invitations and reusable invitation links that they have
Previously, non-admin users could only create email invitations, and created. Previously, non-admin users could only create email invitations, and
therefore the response did not include reusable invitation links for these users. therefore the response did not include reusable invitation links for these
users.
* `DELETE /invites/multiuse/{invite_id}`: Non-admin users can now revoke * `DELETE /invites/multiuse/{invite_id}`: Non-admin users can now revoke
reusable invitation links they have created. Previously, only admin users could reusable invitation links they have created. Previously, only admin users could
@ -611,8 +612,8 @@ No changes; feature level used for Zulip 7.0 release.
**Feature level 180** **Feature level 180**
* `POST /invites`: Added support for invitations specifying the empty * [`POST /invites`](/api/send-invites): Added support for invitations specifying
list as the user's initial stream subscriptions. Previously, this the empty list as the user's initial stream subscriptions. Previously, this
returned an error. This change was also backported to Zulip 6.2, and returned an error. This change was also backported to Zulip 6.2, and
is available at feature levels 157-158 as well. is available at feature levels 157-158 as well.
@ -848,8 +849,8 @@ releases.
**Feature level 157** **Feature level 157**
* `POST /invites`: Added support for invitations specifying the empty * [`POST /invites`](/api/send-invites): Added support for invitations specifying
list as the user's initial stream subscriptions. Previously, this the empty list as the user's initial stream subscriptions. Previously, this
returned an error. This change was backported from the Zulip 7.0 returned an error. This change was backported from the Zulip 7.0
branch, and thus is available at feature levels 157-158 and 180+. branch, and thus is available at feature levels 157-158 and 180+.
@ -1107,8 +1108,8 @@ user's profile.
**Feature level 126** **Feature level 126**
* `POST /invites`, `POST /invites/multiuse`: Replaced `invite_expires_in_days` * [`POST /invites`](/api/send-invites), `POST /invites/multiuse`: Replaced
parameter with `invite_expires_in_minutes`. `invite_expires_in_days` parameter with `invite_expires_in_minutes`.
**Feature level 125** **Feature level 125**
@ -1167,9 +1168,9 @@ No changes; feature level used for Zulip 5.0 release.
**Feature level 117** **Feature level 117**
* `POST /invites`, `POST /invites/multiuse`: Added support for passing * [`POST /invites`](/api/send-invites), `POST /invites/multiuse`: Added
`null` as the `invite_expires_in_days` parameter to request an support for passing `null` as the `invite_expires_in_days` parameter
invitation that never expires. to request an invitation that never expires.
**Feature level 116** **Feature level 116**
@ -1327,7 +1328,7 @@ No changes; feature level used for Zulip 5.0 release.
* [`PATCH /realm/user_settings_defaults`](/api/update-realm-user-settings-defaults): * [`PATCH /realm/user_settings_defaults`](/api/update-realm-user-settings-defaults):
Added new endpoint to update default values of user settings in a realm. Added new endpoint to update default values of user settings in a realm.
* `POST /invites`, `POST /invites/multiuse`: Added * [`POST /invites`](/api/send-invites), `POST /invites/multiuse`: Added
`invite_expires_in_days` parameter encoding the number days before `invite_expires_in_days` parameter encoding the number days before
the invitation should expire. the invitation should expire.
@ -1588,8 +1589,8 @@ No changes; feature level used for Zulip 4.0 release.
**Feature level 61** **Feature level 61**
* `POST /invites`, `POST /invites/multiuse`: Added support for * [`POST /invites`](/api/send-invites), `POST /invites/multiuse`: Added
inviting users as moderators. support for inviting users as moderators.
**Feature level 60** **Feature level 60**
@ -1867,8 +1868,8 @@ No changes; feature level used for Zulip 3.0 release.
encoded as integers; (previously the implementation could send encoded as integers; (previously the implementation could send
floats incorrectly suggesting that microsecond precision is floats incorrectly suggesting that microsecond precision is
relevant). relevant).
* `GET /invites`: Now encodes the user ID of the person who created * [`GET /invites`](/api/get-invites): Now encodes the user ID of the person
the invitation as `invited_by_user_id`, replacing the previous who created the invitation as `invited_by_user_id`, replacing the previous
`ref` field (which had that user's Zulip display email address). `ref` field (which had that user's Zulip display email address).
* [`POST /register`](/api/register-queue): The encoding of an * [`POST /register`](/api/register-queue): The encoding of an
unlimited `realm_message_retention_days` in the response was changed unlimited `realm_message_retention_days` in the response was changed

View File

@ -88,6 +88,11 @@
* [Add alert words](/api/add-alert-words) * [Add alert words](/api/add-alert-words)
* [Remove alert words](/api/remove-alert-words) * [Remove alert words](/api/remove-alert-words)
#### Invitations
* [Get all invitations](/api/get-invites)
* [Send invitations](/api/send-invites)
#### Server & organizations #### Server & organizations
* [Get server settings](/api/get-server-settings) * [Get server settings](/api/get-server-settings)

View File

@ -303,6 +303,32 @@ def get_user_by_email(client: Client) -> None:
validate_against_openapi_schema(result, "/users/{email}", "get", "200") validate_against_openapi_schema(result, "/users/{email}", "get", "200")
@openapi_test_function("/invites:get")
def get_invitations(client: Client) -> None:
# {code_example|start}
# Get all invitations
result = client.call_endpoint(url="/invites", method="GET")
# {code_example|end}
validate_against_openapi_schema(result, "/invites", "get", "200")
@openapi_test_function("/invites:post")
def send_invitations(client: Client) -> None:
# {code_example|start}
# Send invitations
request = {
"invitee_emails": "example@zulip.com, logan@zulip.com",
"invite_expires_in_minutes": 14400,
"invite_as": 400,
"stream_ids": [1, 8, 9],
}
result = client.call_endpoint(url="/invites", method="POST", request=request)
# {code_example|end}
validate_against_openapi_schema(result, "/invites", "post", "200")
@openapi_test_function("/users/{user_id}:get") @openapi_test_function("/users/{user_id}:get")
def get_single_user(client: Client) -> None: def get_single_user(client: Client) -> None:
ensure_users([8], ["cordelia"]) ensure_users([8], ["cordelia"])
@ -1658,6 +1684,11 @@ def test_errors(client: Client) -> None:
test_invalid_stream_error(client) test_invalid_stream_error(client)
def test_invitations(client: Client) -> None:
send_invitations(client)
get_invitations(client)
def test_the_api(client: Client, nonadmin_client: Client, owner_client: Client) -> None: def test_the_api(client: Client, nonadmin_client: Client, owner_client: Client) -> None:
get_user_agent(client) get_user_agent(client)
test_users(client, owner_client) test_users(client, owner_client)
@ -1666,6 +1697,7 @@ def test_the_api(client: Client, nonadmin_client: Client, owner_client: Client)
test_queues(client) test_queues(client)
test_server_organizations(client) test_server_organizations(client)
test_errors(client) test_errors(client)
test_invitations(client)
sys.stdout.flush() sys.stdout.flush()
if REGISTERED_TEST_FUNCTIONS != CALLED_TEST_FUNCTIONS: if REGISTERED_TEST_FUNCTIONS != CALLED_TEST_FUNCTIONS:

View File

@ -11785,6 +11785,235 @@ paths:
responses: responses:
"200": "200":
$ref: "#/components/responses/SimpleSuccess" $ref: "#/components/responses/SimpleSuccess"
/invites:
get:
operationId: get-invites
summary: Get all invitations
tags: ["invites"]
description: |
Fetch all unexpired [invitations](/help/invite-new-users) (i.e. email
invitations and reusable invitation links) that can be managed by the user.
Note that administrators can manage invitations that were created by other users.
**Changes**: Prior to Zulip 8.0 (feature level 209), non-admin users could
only create email invitations, and therefore the response would never include
reusable invitation links for these users.
responses:
"200":
description: Success.
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/JsonSuccessBase"
- additionalProperties: false
properties:
result: {}
msg: {}
ignored_parameters_unsupported: {}
invites:
type: array
description: |
An array of objects, each representing a single unexpired
[invitation](/help/invite-new-users).
items:
$ref: "#/components/schemas/Invite"
example:
{
"result": "success",
"msg": "",
"invites":
[
{
email: "example@zulip.com",
expiry_date: null,
id: 1,
invited: 1710606654,
invited_as: 200,
invited_by_user_id: 9,
is_multiuse: false,
},
{
expiry_date: 1711463862,
id: 1,
invited: 1710599862,
invited_as: 400,
invited_by_user_id: 9,
is_multiuse: true,
link_url: "http://localhost:9991/join/6i4324pzbtyvmwv5nwyghoof/",
},
],
}
post:
operationId: send-invites
summary: Send invitations
tags: ["invites"]
description: |
Send [invitations](/help/invite-new-users) to specified email addresses.
**Changes**: In Zulip 6.0 (feature level 126), the `invite_expires_in_days`
parameter was removed and replaced by `invite_expires_in_minutes`.
In Zulip 5.0 (feature level 117), added support for passing `null` as
the `invite_expires_in_days` parameter to request an invitation that never
expires.
In Zulip 5.0 (feature level 96), the `invite_expires_in_days` parameter was
added which specified the number of days before the invitation would expire.
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
invitee_emails:
description: |
The string containing the email addresses, separated by commas or
newlines, that will be sent an invitation.
type: string
example: example@zulip.com, logan@zulip.com
invite_expires_in_minutes:
description: |
The number of minutes before the invitation will expire. If `null`, the
invitation will never expire. If unspecified, the server will use a default
value (`INVITATION_LINK_VALIDITY_MINUTES`) for when the invitation will expire.
**Changes**: New in Zulip 6.0 (feature level 126). Previously, there was an
`invite_expires_in_days` parameter, which specified the duration in days instead
of minutes.
type: integer
nullable: true
example: 14400
invite_as:
description: |
The [organization-level role](/api/roles-and-permissions) of the user that is
created when the invitation is accepted.
Users can only send invitations for
[roles with equal or stricter restrictions](/api/roles-and-permissions#permission-levels)
as their own. For example, a moderator cannot invite someone to be an owner
or administrator, but they can invite them to be a moderator or member.
**Changes**: In Zulip 4.0 (feature level 61), added support for inviting
users as moderators.
type: integer
enum:
- 100
- 200
- 300
- 400
- 600
default: 400
example: 600
stream_ids:
description: |
A list containing the [IDs of the streams](/api/get-stream-id) that the
newly created user will be automatically subscribed to if the invitation
is accepted. If the list is empty, then the new user will not be
subscribed to any streams.
**Changes**: Before Zulip 7.0 (feature level 180), specifying `stream_ids`
as an empty list resulted in an error.
type: array
items:
type: integer
example: [1, 2]
required:
- invitee_emails
- stream_ids
encoding:
invite_expires_in_minutes:
contentType: application/json
stream_ids:
contentType: application/json
responses:
"200":
description: Success.
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/JsonSuccessBase"
- additionalProperties: false
properties:
result: {}
msg: {}
ignored_parameters_unsupported: {}
example: {"msg": "", "result": "success"}
"400":
description: Bad request.
content:
application/json:
schema:
oneOf:
- allOf:
- $ref: "#/components/schemas/InvitationFailedError"
- example:
{
"result": "error",
"msg": "Some of those addresses are already using Zulip, so we didn't send them an invitation. We did send invitations to everyone else!",
"errors":
[
[
"hamlet@zulip.com",
"Already has an account.",
false,
],
],
"sent_invitations": true,
"license_limit_reached": false,
"daily_limit_reached": false,
"code": "INVITATION_FAILED",
}
description: |
An example JSON error response for when some of the specified email addresses
have existing Zulip accounts.
- allOf:
- $ref: "#/components/schemas/CodedError"
- example:
{
"code": "BAD_REQUEST",
"msg": "Insufficient permission",
"result": "error",
}
description: |
An example JSON error response for when the user doesn't have permission
to send invitations.
- allOf:
- $ref: "#/components/schemas/CodedError"
- example:
{
"code": "BAD_REQUEST",
"msg": "You must specify at least one email address.",
"result": "error",
}
description: |
An example JSON error response for when no email address is specified.
- allOf:
- $ref: "#/components/schemas/CodedError"
- example:
{
"code": "BAD_REQUEST",
"msg": "Stream does not exist with id: 11. No invites were sent.",
"result": "error",
}
description: |
An example JSON error response for when any of the specified streams
does not exist or the user does not have permission to access one of
the targeted streams.
- allOf:
- $ref: "#/components/schemas/CodedError"
- example:
{
"code": "BAD_REQUEST",
"msg": "You do not have permission to subscribe other users to streams.",
"result": "error",
}
description: |
An example JSON error response for when the user doesn't have permission
to subscribe other users to streams and `stream_ids` is not empty.
/register: /register:
post: post:
operationId: register-queue operationId: register-queue
@ -19315,6 +19544,60 @@ components:
New in Zulip 8.0 (feature level 191). Previously, groups New in Zulip 8.0 (feature level 191). Previously, groups
could be mentioned if and only if they were not system groups. could be mentioned if and only if they were not system groups.
Invite:
type: object
description: |
A dictionary containing details about an [invitation](/help/invite-new-users).
additionalProperties: false
properties:
id:
type: integer
description: |
The unique ID of the invitation.
invited_by_user_id:
type: integer
description: |
The [user ID](/api/get-user) of the user who created the invitation.
**Changes**: New in Zulip 3.0 (feature level 22), replacing the `ref`
field which contained the Zulip display email address of the user who
created the invitation.
invited:
type: integer
description: |
The UNIX timestamp for when the invitation was created, in UTC seconds.
expiry_date:
type: integer
nullable: true
description: |
The UNIX timestamp for when the invitation will expire, in UTC seconds.
If `null`, the invitation never expires.
invited_as:
type: integer
enum:
- 100
- 200
- 300
- 400
- 600
description: |
The [organization-level role](/api/roles-and-permissions) of the user that
is created when the invitation is accepted.
email:
type: string
description: |
The email address the invitation was sent to. This will not be present when
`is_multiuse` is `true` (i.e. the invitation is a reusable invitation link).
link_url:
type: string
description: |
The URL of the reusable invitation link. This will not be present when
`is_multiuse` is `false` (i.e. the invitation is an email invitation).
is_multiuse:
type: boolean
description: |
A boolean specifying whether the [invitation](/help/invite-new-users) is a
reusable invitation link or an email invitation.
Subscriptions: Subscriptions:
type: object type: object
additionalProperties: false additionalProperties: false
@ -20682,6 +20965,41 @@ components:
## Invalid API key ## Invalid API key
A typical failed JSON response for when the API key is invalid: A typical failed JSON response for when the API key is invalid:
InvitationFailedError:
allOf:
- $ref: "#/components/schemas/CodedErrorBase"
- additionalProperties: false
properties:
result: {}
msg: {}
code: {}
errors:
type: array
items:
type: array
items:
oneOf:
- type: string
- type: boolean
description: |
An array of arrays of length 3, where each inner array consists of (a) an email
address that was skipped while sending invitations, (b) the corresponding error
message, and (c) a boolean which is `true` when the email address already uses Zulip
and the corresponding user is deactivated in the organization.
sent_invitations:
description: |
A boolean specifying whether any invitations were sent.
type: boolean
daily_limit_reached:
type: boolean
description: |
A boolean specifying whether the limit on the number of invitations that can
be sent in the organization in a day has been reached.
license_limit_reached:
type: boolean
description: |
A boolean specifying whether the organization have enough unused Zulip licenses
to invite specified number of users.
MissingArgumentError: MissingArgumentError:
allOf: allOf:
- $ref: "#/components/schemas/CodedErrorBase" - $ref: "#/components/schemas/CodedErrorBase"

View File

@ -239,7 +239,6 @@ class OpenAPIArgumentsTest(ZulipTestCase):
"/default_stream_groups/{group_id}", "/default_stream_groups/{group_id}",
"/default_stream_groups/{group_id}/streams", "/default_stream_groups/{group_id}/streams",
# Administer invitations # Administer invitations
"/invites",
"/invites/multiuse", "/invites/multiuse",
"/invites/{prereg_id}", "/invites/{prereg_id}",
"/invites/{prereg_id}/resend", "/invites/{prereg_id}/resend",
@ -1023,6 +1022,7 @@ class OpenAPIAttributesTest(ZulipTestCase):
"webhooks", "webhooks",
"scheduled_messages", "scheduled_messages",
"mobile", "mobile",
"invites",
] ]
paths = OpenAPISpec(OPENAPI_SPEC_PATH).openapi()["paths"] paths = OpenAPISpec(OPENAPI_SPEC_PATH).openapi()["paths"]
for path, path_item in paths.items(): for path, path_item in paths.items():