remote-support: Add ability to deactivate and reactivate remote servers.

This commit is contained in:
Lauryn Menard 2024-03-06 18:23:20 +01:00 committed by Tim Abbott
parent 33484e7ac3
commit f3906f4a2f
4 changed files with 137 additions and 0 deletions

View File

@ -573,6 +573,70 @@ class TestRemoteServerSupportEndpoint(ZulipTestCase):
self.assertEqual(plan.tier, CustomerPlan.TIER_SELF_HOSTED_LEGACY)
self.assertEqual(next_plan.tier, CustomerPlan.TIER_SELF_HOSTED_BASIC)
def test_support_deactivate_remote_server(self) -> None:
iago = self.example_user("iago")
self.login_user(iago)
# A remote server cannot be deactivated when an upgrade is scheduled.
remote_server_with_upgrade = RemoteZulipServer.objects.get(hostname="zulip-4.example.com")
self.assertFalse(remote_server_with_upgrade.deactivated)
result = self.client_post(
"/activity/remote/support",
{
"remote_server_id": f"{remote_server_with_upgrade.id}",
"remote_server_status": "deactivated",
},
)
self.assert_in_success_response(
[
f"Cannot deactivate remote server ({remote_server_with_upgrade.hostname}) that has active or scheduled plans."
],
result,
)
remote_server_with_upgrade.refresh_from_db()
self.assertFalse(remote_server_with_upgrade.deactivated)
remote_server_no_upgrade = RemoteZulipServer.objects.get(hostname="zulip-5.example.com")
self.assertFalse(remote_server_no_upgrade.deactivated)
result = self.client_post(
"/activity/remote/support",
{
"remote_server_id": f"{remote_server_no_upgrade.id}",
"remote_server_status": "deactivated",
},
)
self.assert_in_success_response(
[f"Remote server ({remote_server_no_upgrade.hostname}) deactivated."], result
)
remote_server_no_upgrade.refresh_from_db()
self.assertTrue(remote_server_no_upgrade.deactivated)
audit_log = RemoteZulipServerAuditLog.objects.filter(
event_type=RemoteZulipServerAuditLog.REMOTE_SERVER_DEACTIVATED
).last()
assert audit_log is not None
self.assertEqual(audit_log.server, remote_server_no_upgrade)
def test_support_reactivate_remote_server(self) -> None:
iago = self.example_user("iago")
self.login_user(iago)
remote_server = RemoteZulipServer.objects.get(hostname="zulip-0.example.com")
self.assertTrue(remote_server.deactivated)
result = self.client_post(
"/activity/remote/support",
{"remote_server_id": f"{remote_server.id}", "remote_server_status": "active"},
)
self.assert_in_success_response(
[f"Remote server ({remote_server.hostname}) reactivated."], result
)
remote_server.refresh_from_db()
self.assertFalse(remote_server.deactivated)
audit_log = RemoteZulipServerAuditLog.objects.filter(
event_type=RemoteZulipServerAuditLog.REMOTE_SERVER_REACTIVATED
).last()
assert audit_log is not None
self.assertEqual(audit_log.server, remote_server)
class TestSupportEndpoint(ZulipTestCase):
def create_customer_and_plan(self, realm: Realm, monthly: bool = False) -> Customer:

View File

@ -26,10 +26,13 @@ from corporate.lib.stripe import (
RealmBillingSession,
RemoteRealmBillingSession,
RemoteServerBillingSession,
ServerDeactivateWithExistingPlanError,
SupportRequestError,
SupportType,
SupportViewRequest,
cents_to_dollar_string,
do_deactivate_remote_server,
do_reactivate_remote_server,
format_discount_percentage,
)
from corporate.lib.support import (
@ -534,6 +537,9 @@ def remote_servers_support(
default=None, str_validator=check_string_in(VALID_MODIFY_PLAN_METHODS)
),
delete_fixed_price_next_plan: bool = REQ(default=False, json_validator=check_bool),
remote_server_status: Optional[str] = REQ(
default=None, str_validator=check_string_in(VALID_STATUS_VALUES)
),
) -> HttpResponse:
context: Dict[str, Any] = {}
@ -608,6 +614,28 @@ def remote_servers_support(
support_view_request = SupportViewRequest(
support_type=SupportType.delete_fixed_price_next_plan,
)
elif remote_server_status:
assert remote_server is not None
remote_server_status_billing_session = RemoteServerBillingSession(
support_staff=acting_user, remote_server=remote_server
)
if remote_server_status == "active":
do_reactivate_remote_server(remote_server)
context["success_message"] = (
f"Remote server ({remote_server.hostname}) reactivated."
)
else:
assert remote_server_status == "deactivated"
try:
do_deactivate_remote_server(remote_server, remote_server_status_billing_session)
context["success_message"] = (
f"Remote server ({remote_server.hostname}) deactivated."
)
except ServerDeactivateWithExistingPlanError:
context["error_message"] = (
f"Cannot deactivate remote server ({remote_server.hostname}) that has active or scheduled plans."
)
if support_view_request is not None:
if remote_realm_support_request:
try:

View File

@ -146,6 +146,22 @@
{% endwith %}
</div>
{% endif %}
{% if remote_server.deactivated %}
<form method="POST" class="reactivate-remote-server-form">
{{ csrf_input }}
<input type="hidden" name="remote_server_id" value="{{ remote_server.id }}" />
<input type="hidden" name="remote_server_status" value="active" />
<button class="reactivate-remote-server-button"><b>Reactivate</b>: {{ remote_server.hostname }}</button>
</form>
{% else %}
<form method="POST" class="deactivate-remote-server-form">
{{ csrf_input }}
<input type="hidden" name="remote_server_id" value="{{ remote_server.id }}" />
<input type="hidden" name="remote_server_status" value="deactivated" />
<button class="deactivate-remote-server-button"><b>Deactivate</b>: {{ remote_server.hostname }}</button>
</form>
{% endif %}
</div>
<div class="remote-realms-section">
{% if remote_realms[remote_server.id] == [] %}

View File

@ -171,6 +171,8 @@ tr.admin td:first-child {
}
}
.reactivate-remote-server-button,
.deactivate-remote-server-button,
.delete-next-fixed-price-plan-button,
.approve-sponsorship-button,
.support-search-button,
@ -243,6 +245,7 @@ tr.admin td:first-child {
}
}
.deactivate-remote-server-button,
.delete-next-fixed-price-plan-button,
.delete-user-button,
.scrub-realm-button {
@ -260,6 +263,30 @@ tr.admin td:first-child {
}
}
.reactivate-remote-server-button {
color: hsl(0deg 0% 100%);
background-color: hsl(35deg 70% 56%);
border: 1px solid hsl(35deg 70% 50%);
border-radius: 2px;
&:hover,
&:focus,
&:active {
color: hsl(0deg 0% 100%);
background-color: hsl(35deg 82% 40%);
border: 1px solid hsl(35deg 82% 30%);
}
}
.reactivate-remote-server-button,
.deactivate-remote-server-button {
font-size: 15px;
line-height: 1.8;
max-width: 550px;
overflow: hidden;
text-overflow: ellipsis;
}
.remote-support-query-result.remote-server-deactivated {
background-color: hsl(2deg 64% 58%);
@ -280,6 +307,8 @@ tr.admin td:first-child {
text-align: center;
}
.reactivate-remote-server-form,
.deactivate-remote-server-form,
.realm-support-information,
.remote-server-information,
.remote-realm-information {