|
52 | 52 | ) |
53 | 53 | from chat_sdk.emoji import convert_emoji_placeholders, emoji_to_slack, resolve_emoji_from_slack |
54 | 54 | from chat_sdk.logger import ConsoleLogger, Logger |
55 | | -from chat_sdk.modals import ModalElement, SelectOptionElement |
| 55 | +from chat_sdk.modals import ModalElement, OptionsLoadGroup, SelectOptionElement |
56 | 56 | from chat_sdk.shared.adapter_utils import extract_card, extract_files |
57 | 57 | from chat_sdk.shared.errors import AdapterRateLimitError, AuthenticationError, ValidationError |
58 | 58 | from chat_sdk.types import ( |
@@ -1098,24 +1098,70 @@ def _late_error(t: asyncio.Task[Any]) -> None: |
1098 | 1098 |
|
1099 | 1099 | return self._options_load_response(result if result is not None else []) |
1100 | 1100 |
|
1101 | | - def _options_load_response(self, options_list: list[SelectOptionElement]) -> dict[str, Any]: |
1102 | | - """Serialize ``SelectOptionElement`` entries to a Slack JSON response.""" |
1103 | | - slack_options: list[dict[str, Any]] = [] |
1104 | | - for opt in options_list[:100]: |
1105 | | - entry: dict[str, Any] = { |
1106 | | - "text": {"type": "plain_text", "text": opt.get("label", "")}, |
1107 | | - "value": opt.get("value", ""), |
| 1101 | + def _options_load_response( |
| 1102 | + self, |
| 1103 | + result: list[SelectOptionElement] | list[OptionsLoadGroup], |
| 1104 | + ) -> dict[str, Any]: |
| 1105 | + """Serialize a flat option list or grouped option list to a Slack JSON response. |
| 1106 | +
|
| 1107 | + Mirrors upstream ``optionsLoadResponse``: when the first entry has an |
| 1108 | + ``options`` key it is treated as a list of :class:`OptionsLoadGroup` |
| 1109 | + and rendered as ``option_groups``; otherwise it's a flat list of |
| 1110 | + :class:`SelectOptionElement` rendered as ``options``. Slack's spec is |
| 1111 | + explicit that the two are mutually exclusive (only one may appear in |
| 1112 | + the response body). |
| 1113 | + """ |
| 1114 | + # Detect grouped form (TS: ``"options" in result[0]``). A grouped |
| 1115 | + # entry is a dict with an ``options`` list inside it; a flat entry is |
| 1116 | + # a dict with ``label``/``value`` keys. |
| 1117 | + is_groups = ( |
| 1118 | + len(result) > 0 |
| 1119 | + and isinstance(result[0], dict) |
| 1120 | + and "options" in result[0] |
| 1121 | + and isinstance(result[0].get("options"), list) |
| 1122 | + ) |
| 1123 | + |
| 1124 | + if is_groups: |
| 1125 | + groups_in = cast("list[OptionsLoadGroup]", result)[:100] |
| 1126 | + slack_groups: list[dict[str, Any]] = [] |
| 1127 | + for group in groups_in: |
| 1128 | + group_options = group.get("options", [])[:100] |
| 1129 | + slack_groups.append( |
| 1130 | + { |
| 1131 | + # Slack spec: group label is plain_text, max 75 chars. |
| 1132 | + "label": {"type": "plain_text", "text": group.get("label", "")[:75]}, |
| 1133 | + "options": [self._select_option_to_slack(opt) for opt in group_options], |
| 1134 | + } |
| 1135 | + ) |
| 1136 | + return { |
| 1137 | + "body": json.dumps({"option_groups": slack_groups}), |
| 1138 | + "status": 200, |
| 1139 | + "headers": {"Content-Type": "application/json"}, |
1108 | 1140 | } |
1109 | | - desc = opt.get("description") |
1110 | | - if desc: |
1111 | | - entry["description"] = {"type": "plain_text", "text": desc} |
1112 | | - slack_options.append(entry) |
| 1141 | + |
| 1142 | + flat = cast("list[SelectOptionElement]", result)[:100] |
1113 | 1143 | return { |
1114 | | - "body": json.dumps({"options": slack_options}), |
| 1144 | + "body": json.dumps({"options": [self._select_option_to_slack(opt) for opt in flat]}), |
1115 | 1145 | "status": 200, |
1116 | 1146 | "headers": {"Content-Type": "application/json"}, |
1117 | 1147 | } |
1118 | 1148 |
|
| 1149 | + @staticmethod |
| 1150 | + def _select_option_to_slack(opt: SelectOptionElement) -> dict[str, Any]: |
| 1151 | + """Convert a :class:`SelectOptionElement` to Slack's option object shape. |
| 1152 | +
|
| 1153 | + Mirrors upstream ``selectOptionToSlackOption`` — the ``description`` |
| 1154 | + key is omitted (not set to ``null``) when not provided. |
| 1155 | + """ |
| 1156 | + entry: dict[str, Any] = { |
| 1157 | + "text": {"type": "plain_text", "text": opt.get("label", "")}, |
| 1158 | + "value": opt.get("value", ""), |
| 1159 | + } |
| 1160 | + desc = opt.get("description") |
| 1161 | + if desc: |
| 1162 | + entry["description"] = {"type": "plain_text", "text": desc} |
| 1163 | + return entry |
| 1164 | + |
1119 | 1165 | # ================================================================== |
1120 | 1166 | # View submission / close |
1121 | 1167 | # ================================================================== |
|
0 commit comments