Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions corporate/lib/stripe.py
Original file line number Diff line number Diff line change
Expand Up @@ -1370,7 +1370,7 @@ def create_stripe_invoice_and_charge(
if isinstance(e, stripe.CardError):
raise StripeCardError("card error", e.user_message)
else: # nocoverage
raise e
raise

assert stripe_invoice.id is not None
return stripe_invoice.id
Expand Down Expand Up @@ -4151,7 +4151,7 @@ def create_complimentary_access_plan(
plan_tier = CustomerPlan.TIER_SELF_HOSTED_LEGACY
if isinstance(self, RealmBillingSession): # nocoverage
# TODO implement a complimentary access plan/tier for Zulip Cloud.
return None
return
customer = self.update_or_create_customer()

complimentary_access_plan = self.create_customer_plan(
Expand Down Expand Up @@ -6012,7 +6012,7 @@ def invoice_plans_as_needed(event_time: datetime | None = None) -> None:
stack_info=True,
)
else:
billing_logger.exception(e, stack_info=True) # nocoverage
billing_logger.exception("Error while invoicing", stack_info=True) # nocoverage


def is_realm_on_free_trial(realm: Realm) -> bool:
Expand Down
10 changes: 10 additions & 0 deletions corporate/lib/test_stripe_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
stripe_get_customer,
)
from corporate.models.customers import Customer
from corporate.models.licenses import LicenseLedger
from corporate.models.plans import CustomerPlan
from corporate.models.stripe_state import Invoice
from zerver.actions.users import do_deactivate_user
Expand Down Expand Up @@ -1046,3 +1047,12 @@ def client_billing_patch(self, url_suffix: str, info: Mapping[str, Any] = {}) ->
else:
response = self.client_patch(url, info)
return response

def check_last_ledger_entry_license_counts(
self, plan: CustomerPlan, licenses: int, licenses_at_next_renewal: int
) -> LicenseLedger:
ledger_entry = LicenseLedger.objects.filter(plan=plan).order_by("-id").first()
assert ledger_entry is not None
self.assertEqual(ledger_entry.licenses, licenses)
self.assertEqual(ledger_entry.licenses_at_next_renewal, licenses_at_next_renewal)
return ledger_entry
435 changes: 145 additions & 290 deletions corporate/tests/test_stripe.py

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions corporate/views/upgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def upgrade(
license_management,
licenses,
)
raise e
raise
except Exception:
billing_logger.exception("Uncaught exception in billing:", stack_info=True)
error_message = BillingError.CONTACT_SUPPORT.format(email=settings.ZULIP_ADMINISTRATOR)
Expand Down Expand Up @@ -118,7 +118,7 @@ def remote_realm_upgrade(
license_management,
licenses,
)
raise e
raise
except Exception: # nocoverage
billing_logger.exception("Uncaught exception in billing:", stack_info=True)
error_message = BillingError.CONTACT_SUPPORT.format(email=settings.ZULIP_ADMINISTRATOR)
Expand Down Expand Up @@ -168,7 +168,7 @@ def remote_server_upgrade(
license_management,
licenses,
)
raise e
raise
except Exception: # nocoverage
billing_logger.exception("Uncaught exception in billing:", stack_info=True)
error_message = BillingError.CONTACT_SUPPORT.format(email=settings.ZULIP_ADMINISTRATOR)
Expand Down
Empty file added docs/__init__.py
Empty file.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
"@types/convert-source-map": "^2.0.3",
"@types/css-tree": "^2.3.11",
"@types/eslint-config-prettier": "^6.11.3",
"@types/estree": "^1.0.9",
"@types/gtag.js": "^0.0.20",
"@types/is-url": "^1.2.32",
"@types/jquery": "^4.0.0",
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions puppet/kandra/files/statuspage-pusher
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ def main() -> None:
for metric_id, query in metrics.items():
try:
update_metric(metric_id, query, page_id, oauth_token)
except Exception as e:
logging.exception(e)
except Exception:
logging.exception("Error while updating metric")
time.sleep(30)


Expand Down
4 changes: 2 additions & 2 deletions puppet/zulip/files/postgresql/wal-g-exporter
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ class WalGPrometheusServer(BaseHTTPRequestHandler):
(t("finish_time") - t("start_time")) / timedelta(seconds=1), labels
)
backup_ok(1)
except Exception as e:
logging.exception(e)
except Exception:
logging.exception("Error while getting backup information")
finally:
self.print_metrics()
self.log_message(
Expand Down
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ target-version = "py310"

[tool.ruff.lint]
# See https://github.com/astral-sh/ruff#rules for error code definitions.
select = [
extend-select = [
"ANN", # annotations
"B", # bugbear
"C4", # comprehensions
Expand All @@ -477,6 +477,7 @@ select = [
"FURB", # refurbishing
"G", # logging format
"I", # import sorting
"INP", # implicit namespace package
"INT", # gettext
"ISC", # string concatenation
"LOG", # logging
Expand All @@ -503,6 +504,7 @@ ignore = [
"ANN401", # Dynamically typed expressions (typing.Any) are disallowed
"B007", # Loop control variable not used within the loop body
"B904", # Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
"BLE001", # Do not catch blind exception
"C408", # Unnecessary `dict` call (rewrite as a literal)
"COM812", # Trailing comma missing
"DJ001", # Avoid using `null=True` on string-based fields
Expand Down Expand Up @@ -550,6 +552,8 @@ ignore = [
"TC002", # Move third-party import into a type-checking block
"TC003", # Move standard library import into a type-checking block
"TC006", # Add quotes to type expression in `typing.cast()`
"TRY002", # Create your own exception
"TRY004", # Prefer `TypeError` exception for invalid type
]

[tool.ruff.lint.flake8-bandit]
Expand Down
4 changes: 4 additions & 0 deletions templates/zerver/development/integrations_dev_panel.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
<label class="optional"><b>Topic</b></label>
<input id="topic_name" type="text" />
</div>
<div>
<label class="optional"><b>Webhook Secret</b></label>
<input id="webhook_secret" type="text" />
</div>
</div>

<br />
Expand Down
Empty file added tools/droplets/__init__.py
Empty file.
Empty file added tools/oneclickapps/__init__.py
Empty file.
2 changes: 1 addition & 1 deletion tools/run-dev
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ async def serve() -> None:

setup_routes(options.help_center_static_build, options.help_center_dev_server)

children.extend(subprocess.Popen(cmd) for cmd in server_processes())
children.extend(subprocess.Popen(cmd) for cmd in server_processes()) # noqa: ASYNC220

session = aiohttp.ClientSession()
runner = web.AppRunner(app, auto_decompress=False, handler_cancellation=True)
Expand Down
Empty file added tools/setup/emoji/__init__.py
Empty file.
1 change: 1 addition & 0 deletions web/src/bot_type_values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export const INCOMING_WEBHOOK_BOT_TYPE_INT = 2;
export const OUTGOING_WEBHOOK_BOT_TYPE_INT = 3;

// String forms used as HTML form values.
export const INCOMING_WEBHOOK_BOT_TYPE = "2";
export const OUTGOING_WEBHOOK_BOT_TYPE = "3";
export const EMBEDDED_BOT_TYPE = "4";
7 changes: 6 additions & 1 deletion web/src/compose_closed_ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ import * as stream_data from "./stream_data.ts";
import type {StreamSubscription} from "./sub_store.ts";
import * as util from "./util.ts";

// `label_text` is a flat, pre-formatted string used by the call-creation
// paths in compose_call_ui.ts to build a meeting/room name.
// The reply-button template uses the structured `stream` / `topic_display_name`
// fields instead, so it can render the decorated channel icon.
type RecipientLabel = {
label_text?: string;
label_text: string;
has_empty_string_topic?: boolean;
stream?: StreamSubscription;
topic_display_name?: string;
Expand All @@ -31,6 +35,7 @@ function get_stream_recipient_label(stream_id: number, topic: string): Recipient
const topic_display_name = util.get_final_topic_display_name(topic);
if (stream) {
const recipient_label: RecipientLabel = {
label_text: `#${stream.name} > ${topic_display_name}`,
has_empty_string_topic: topic === "",
stream,
topic_display_name,
Expand Down
78 changes: 77 additions & 1 deletion web/src/portico/integrations_dev_panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const integrations_api_response_schema = z.object({

type ServerResponse = z.infer<typeof integrations_api_response_schema>;

let last_computed_header_key: string | null = null; // Tracks the current signature header for auto-clearing when switching integrations

const loaded_fixtures = new Map<string, Fixtures>();
const url_base = "/api/v1/external/";

Expand Down Expand Up @@ -231,11 +233,83 @@ function update_url(): void {
params.set("topic", topic_name);
}
}
const webhook_secret = $<HTMLInputElement>("input#webhook_secret").val()!;
const url = `${url_base}${integration_name}?${params.toString()}`;
url_field!.value = url;

sync_signature_headers(integration_name, webhook_secret);
}
}

return;
function sync_signature_headers(integration_name: string, webhook_secret: string): void {
const $custom_headers_field = $<HTMLTextAreaElement>("textarea#custom_http_headers");
const current_headers_raw = $custom_headers_field.val()?.toString().trim() ?? "";

let headers_object: Record<string, string> = {};
if (current_headers_raw !== "") {
try {
headers_object = z
.record(z.string(), z.string())
.parse(JSON.parse(current_headers_raw));
} catch {
headers_object = {};
}
}

if (last_computed_header_key && Object.hasOwn(headers_object, last_computed_header_key)) {
Reflect.deleteProperty(headers_object, last_computed_header_key);
}

if (webhook_secret.trim() === "") {
last_computed_header_key = null;
if (Object.keys(headers_object).length === 0) {
$custom_headers_field.val("{}");
} else {
$custom_headers_field.val(JSON.stringify(headers_object, null, 4));
}
return;
}

const raw_payload = $<HTMLTextAreaElement>("textarea#fixture_body").val() ?? "";
let cleaned_payload: string;

try {
cleaned_payload = JSON.stringify(JSON.parse(raw_payload));
} catch {
cleaned_payload = raw_payload.trim();
}

channel.post({
url: "/devtools/integrations/recalculate_signature",
data: JSON.stringify({
secret: webhook_secret,
payload: cleaned_payload,
integration_name,
}),
success(raw_data: unknown) {
const data = z
.object({
supported: z.optional(z.boolean()),
clear_signature: z.optional(z.boolean()),
header_key: z.string(),
signature: z.string(),
})
.parse(raw_data);

if (!data.supported || data.clear_signature) {
last_computed_header_key = null;
if (Object.keys(headers_object).length === 0) {
$custom_headers_field.val("{}");
} else {
$custom_headers_field.val(JSON.stringify(headers_object, null, 4));
}
} else {
headers_object[data.header_key] = data.signature;
last_computed_header_key = data.header_key;
$custom_headers_field.val(JSON.stringify(headers_object, null, 4));
}
},
});
}

// API callers: These methods handle communicating with the Python backend API.
Expand Down Expand Up @@ -440,4 +514,6 @@ $(() => {
$("#stream_name").on("change", update_url);

$("#topic_name").on("change", update_url);

$("#webhook_secret").on("change", update_url);
});
40 changes: 31 additions & 9 deletions web/src/settings_bots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as bot_helper from "./bot_helper.ts";
import {
EMBEDDED_BOT_TYPE,
GENERIC_BOT_TYPE,
INCOMING_WEBHOOK_BOT_TYPE,
INCOMING_WEBHOOK_BOT_TYPE_INT,
OUTGOING_WEBHOOK_BOT_TYPE,
OUTGOING_WEBHOOK_BOT_TYPE_INT,
Expand Down Expand Up @@ -298,6 +299,20 @@ export function add_a_new_bot(): void {
formData.append("interface_type", interface_type);
break;
}
case INCOMING_WEBHOOK_BOT_TYPE: {
const config_data: Record<string, string> = {};
$<HTMLInputElement>("#webhook_secret_inputbox input").each(function () {
const key = $(this).attr("name")!;
const raw_val = $(this).val();
if (typeof raw_val === "string" && raw_val.trim() !== "") {
config_data[key] = raw_val.trim();
}
});
if (Object.keys(config_data).length > 0) {
formData.append("config_data", JSON.stringify(config_data));
}
break;
}
case EMBEDDED_BOT_TYPE: {
formData.append("service_name", service_name);
const config_data: Record<string, string> = {};
Expand Down Expand Up @@ -336,7 +351,7 @@ export function add_a_new_bot(): void {
}

function set_up_form_fields(): void {
$("#create_bot_type").val(INCOMING_WEBHOOK_BOT_TYPE_INT);
$("#create_bot_type").val(INCOMING_WEBHOOK_BOT_TYPE).trigger("change");
$("#payload_url_inputbox").hide();
$("#create_payload_url").val("");
$("#service_name_list").hide();
Expand All @@ -362,7 +377,13 @@ export function add_a_new_bot(): void {

$("#payload_url_inputbox").hide();
$("#create_payload_url").removeClass("required");

$("#webhook_secret_inputbox").hide();
switch (bot_type) {
case INCOMING_WEBHOOK_BOT_TYPE: {
$("#webhook_secret_inputbox").show();
break;
}
case OUTGOING_WEBHOOK_BOT_TYPE: {
$("#payload_url_inputbox").show();
$("#create_payload_url").addClass("required");
Expand All @@ -377,7 +398,7 @@ export function add_a_new_bot(): void {
}
}
});

$("#create_bot_type").val(INCOMING_WEBHOOK_BOT_TYPE).trigger("change");
$("#select_service_name").on("change", () => {
$("#config_inputbox").children().hide();
const selected_bot = $<HTMLSelectOneElement>(
Expand Down Expand Up @@ -742,11 +763,12 @@ function set_up_bot_handlers($container: JQuery): void {
add_a_new_bot();
});

$container.find(".download-botserverrc-file").on("click", function () {
$container.find(".download-botserverrc-file").on("click", (e) => {
const currentTarget = e.currentTarget;
void (async () => {
let content = "";
buttons.show_button_loading_indicator($(this));
$(this).prop("disabled", true);
buttons.show_button_loading_indicator($(currentTarget));
$(currentTarget).prop("disabled", true);
for (const bot of bot_data.get_all_bots_for_current_user()) {
if (bot.is_active && bot.bot_type === OUTGOING_WEBHOOK_BOT_TYPE_INT) {
const bot_token = bot_helper.get_outgoing_webhook_token(bot.user_id);
Expand All @@ -755,15 +777,15 @@ function set_up_bot_handlers($container: JQuery): void {
$("#admin-your-bots-list .bot-list-error"),
);
if (!api_key) {
buttons.hide_button_loading_indicator($(this));
$(this).prop("disabled", false);
buttons.hide_button_loading_indicator($(currentTarget));
$(currentTarget).prop("disabled", false);
return;
}
content += generate_botserverrc_content(bot.email, api_key, bot_token);
}
}
buttons.hide_button_loading_indicator($(this));
$(this).prop("disabled", false);
buttons.hide_button_loading_indicator($(currentTarget));
$(currentTarget).prop("disabled", false);

$container
.find(".hidden-botserverrc-download")
Expand Down
Loading