backend: Add `org_type` to realm settings updates and events.

`org_type` already exists as a field in the Realm model and is
used when organizations are created / updated in Zulip Cloud,
via the `/analytics/support` view.

Extends the `PATCH /realm` view to be able update `org_type` as
other realm / organization settings are updated, but using the
special log / action that was created for the analytics view.

Adds a field to the `realm op: update` / `realm op: update_dict`
events, which also means an event is now sent when and if the
`org_type` is updated via the analytics view. This is similar
to how updates to an organization's `plan_type` trigger events.

Adds `realm_org_type` as a realm setting fetched from the
`POST /register` endpoint.
This commit is contained in:
Lauryn Menard 2022-04-11 19:26:16 +02:00 committed by Tim Abbott
parent 9df0f1433e
commit d2207d4ad5
10 changed files with 101 additions and 3 deletions

View File

@ -20,6 +20,12 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 6.0
**Feature level 128**
* [`POST /register`](/api/register-queue), [`GET
/events`](/api/get-events), `PATCH /realm`: Added
`org_type` realm setting.
**Feature level 127**
* [`GET /user_groups`](/api/get-user-groups),[`POST

View File

@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.4.3"
# Changes should be accompanied by documentation explaining what the
# new level means in templates/zerver/api/changelog.md, as well as
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
API_FEATURE_LEVEL = 127
API_FEATURE_LEVEL = 128
# 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

View File

@ -403,6 +403,9 @@ def do_change_realm_org_type(
extra_data={"old_value": old_value, "new_value": org_type},
)
event = dict(type="realm", op="update", property="org_type", value=org_type)
transaction.on_commit(lambda: send_event(realm, event, active_user_ids(realm.id)))
@transaction.atomic(savepoint=False)
def do_change_realm_plan_type(

View File

@ -883,7 +883,7 @@ def check_realm_update(
assert "extra_data" not in event.keys()
if prop in ["notifications_stream_id", "signup_notifications_stream_id"]:
if prop in ["notifications_stream_id", "signup_notifications_stream_id", "org_type"]:
assert isinstance(value, int)
return

View File

@ -286,6 +286,7 @@ def fetch_initial_state_data(
state["server_generation"] = settings.SERVER_GENERATION
state["realm_is_zephyr_mirror_realm"] = realm.is_zephyr_mirror_realm
state["development_environment"] = settings.DEVELOPMENT
state["realm_org_type"] = realm.org_type
state["realm_plan_type"] = realm.plan_type
state["zulip_plan_is_not_limited"] = realm.plan_type != Realm.PLAN_TYPE_LIMITED
state["upgrade_text_for_wide_organization_logo"] = str(Realm.UPGRADE_TEXT_STANDARD)

View File

@ -3929,6 +3929,26 @@ paths:
Since these notifications are sent by the server, this field is
primarily relevant to clients containing UI for changing it.
org_type:
type: integer
description: |
The type of the organization.
- 0 = Unspecified
- 10 = Business
- 20 = Open-source project
- 30 = Education (non-profit)
- 35 = Education (for-profit)
- 40 = Research
- 50 = Event or conference
- 60 = Non-profit (registered)
- 70 = Government
- 80 = Political group
- 90 = Community
- 100 = Personal
- 1000 = Other
**Changes**: New in Zulip 6.0 (feature level 128).
plan_type:
type: integer
description: |
@ -11463,6 +11483,28 @@ paths:
**Changes**: New in Zulip 5.0 (feature level 72). Previously,
this was called `realm_upload_quota`.
realm_org_type:
type: integer
description: |
Present if `realm` is present in `fetch_event_types`.
The type of the organization.
- 0 = Unspecified
- 10 = Business
- 20 = Open-source project
- 30 = Education (non-profit)
- 35 = Education (for-profit)
- 40 = Research
- 50 = Event or conference
- 60 = Non-profit (registered)
- 70 = Government
- 80 = Political group
- 90 = Community
- 100 = Personal
- 1000 = Other
**Changes**: New in Zulip 6.0 (feature level 128).
realm_plan_type:
type: integer
description: |

View File

@ -69,6 +69,7 @@ from zerver.actions.realm_linkifiers import (
from zerver.actions.realm_logo import do_change_logo_source
from zerver.actions.realm_playgrounds import do_add_realm_playground, do_remove_realm_playground
from zerver.actions.realm_settings import (
do_change_realm_org_type,
do_change_realm_plan_type,
do_deactivate_realm,
do_set_realm_authentication_methods,
@ -1739,6 +1740,22 @@ class NormalActionsTest(BaseAction):
check_user_settings_update("events[0]", events[0])
check_update_global_notifications("events[1]", events[1], 1)
def test_realm_update_org_type(self) -> None:
realm = self.user_profile.realm
state_data = fetch_initial_state_data(self.user_profile)
self.assertEqual(state_data["realm_org_type"], Realm.ORG_TYPES["business"]["id"])
events = self.verify_action(
lambda: do_change_realm_org_type(
realm, Realm.ORG_TYPES["government"]["id"], acting_user=self.user_profile
)
)
check_realm_update("events[0]", events[0], "org_type")
state_data = fetch_initial_state_data(self.user_profile)
self.assertEqual(state_data["realm_org_type"], Realm.ORG_TYPES["government"]["id"])
def test_realm_update_plan_type(self) -> None:
realm = self.user_profile.realm

View File

@ -165,6 +165,7 @@ class HomeTest(ZulipTestCase):
"realm_night_logo_url",
"realm_non_active_users",
"realm_notifications_stream_id",
"realm_org_type",
"realm_password_auth_enabled",
"realm_plan_type",
"realm_playgrounds",

View File

@ -1120,6 +1120,26 @@ class RealmAPITest(ZulipTestCase):
with self.subTest(property=prop):
self.do_test_realm_update_api(prop)
# Not in Realm.property_types because org_type has
# a unique RealmAuditLog event_type.
def test_update_realm_org_type(self) -> None:
vals = [t["id"] for t in Realm.ORG_TYPES.values()]
self.set_up_db("org_type", vals[0])
for val in vals[1:]:
realm = self.update_with_api("org_type", val)
self.assertEqual(getattr(realm, "org_type"), val)
realm = self.update_with_api("org_type", vals[0])
self.assertEqual(getattr(realm, "org_type"), vals[0])
# Now we test an invalid org_type id.
invalid_org_type = 1
assert invalid_org_type not in vals
result = self.client_patch("/json/realm", {"org_type": invalid_org_type})
self.assert_json_error(result, "Invalid org_type")
def update_with_realm_default_api(self, name: str, val: Any) -> None:
if not isinstance(val, str):
val = orjson.dumps(val).decode()

View File

@ -1,4 +1,4 @@
from typing import Any, Dict, Optional, Union
from typing import Any, Dict, List, Optional, Union
from django.core.exceptions import ValidationError
from django.http import HttpRequest, HttpResponse
@ -9,6 +9,7 @@ from django.views.decorators.http import require_safe
from confirmation.models import Confirmation, ConfirmationKeyException, get_object_from_key
from zerver.actions.create_realm import do_change_realm_subdomain
from zerver.actions.realm_settings import (
do_change_realm_org_type,
do_deactivate_realm,
do_reactivate_realm,
do_set_realm_authentication_methods,
@ -40,6 +41,8 @@ from zerver.lib.validator import (
from zerver.models import Realm, RealmUserDefault, UserProfile
from zerver.views.user_settings import check_settings_values
ORG_TYPE_IDS: List[int] = [t["id"] for t in Realm.ORG_TYPES.values()]
@require_realm_admin
@has_request_variables
@ -138,6 +141,7 @@ def update_realm(
str_validator=check_capped_string(Realm.MAX_REALM_SUBDOMAIN_LENGTH),
default=None,
),
org_type: Optional[int] = REQ(json_validator=check_int_in(ORG_TYPE_IDS), default=None),
enable_spectator_access: Optional[bool] = REQ(json_validator=check_bool, default=None),
) -> HttpResponse:
realm = user_profile.realm
@ -294,6 +298,10 @@ def update_realm(
do_change_realm_subdomain(realm, string_id, acting_user=user_profile)
data["realm_uri"] = realm.uri
if org_type is not None:
do_change_realm_org_type(realm, org_type, acting_user=user_profile)
data["org_type"] = org_type
return json_success(request, data)