Skip to content

Commit ea52d3c

Browse files
authored
Merge pull request #120 from AUSG/claude/gogo-emoji-deleted-sheet-yvmpw6
2 parents b761a66 + 76db193 commit ea52d3c

7 files changed

Lines changed: 125 additions & 8 deletions

File tree

src/handler/bigchat/abandon_bigchat.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22
from typing import List
33

4+
from implementation.google_spreadsheet_client import WorksheetNotFound
45
from implementation.member_finder import MemberNotFound, MemberLackInfo
56
from implementation.slack_client import Message
67

@@ -57,7 +58,15 @@ def run(self):
5758
)
5859
return False
5960

60-
self.gs_client.delete_row(worksheet_id, member.email)
61+
try:
62+
self.gs_client.delete_row(worksheet_id, member.email)
63+
except WorksheetNotFound:
64+
self.slack_client.send_message(
65+
msg=f"<@{self.user}>, 이 빅챗의 신청 시트를 찾을 수 없어서 등록을 취소하지 못했어. "
66+
f"이미 마감되었거나 삭제된 것 같아. 필요하면 운영진에게 알려줘!",
67+
ts=self.ts,
68+
)
69+
return False
6170

6271
self.slack_client.send_message_only_visible_to_user(
6372
msg=f"<@{self.user}>, 등록을 취소했어.",

src/handler/bigchat/join_bigchat.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import List, Optional
44

55
from config.env_config import envs
6+
from implementation.google_spreadsheet_client import WorksheetNotFound
67
from implementation.member_finder import MemberNotFound, MemberLackInfo
78
from implementation.slack_client import Message
89
from util.bigchat_event import parse_sheet_name, to_gcal_link, ics_token
@@ -105,7 +106,15 @@ def run(self):
105106
)
106107
return False
107108

108-
self.gs_client.append_row(worksheet_id, member.transform_for_spreadsheet())
109+
try:
110+
self.gs_client.append_row(worksheet_id, member.transform_for_spreadsheet())
111+
except WorksheetNotFound:
112+
self.slack_client.send_message(
113+
msg=f"<@{self.user}>, 이 빅챗의 신청 시트를 찾을 수 없어. 이미 마감되었거나 삭제된 것 같아. "
114+
f"모집이 진행 중인 빅챗이라면 운영진에게 알려줘!",
115+
ts=self.ts,
116+
)
117+
return False
109118

110119
self.slack_client.send_message(msg=f"<@{self.user}>, 등록 완료!", ts=self.ts)
111120
msg = strip_multiline(

src/implementation/google_spreadsheet_client.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from dateutil.tz import gettz
66
from gspread import service_account_from_dict, Worksheet, Spreadsheet
7+
from gspread.exceptions import WorksheetNotFound
78
from gspread_formatting import set_column_width
89

910
from config.env_config import envs
@@ -58,7 +59,7 @@ def _create_worksheet(
5859

5960
return worksheet.id
6061

61-
@with_retry()
62+
@with_retry(non_retryable_exceptions=(WorksheetNotFound,))
6263
def append_row(
6364
self,
6465
worksheet_id: int,
@@ -73,12 +74,12 @@ def append_row(
7374

7475
worksheet.append_row(_values)
7576

76-
@with_retry()
77+
@with_retry(non_retryable_exceptions=(WorksheetNotFound,))
7778
def get_values(self, worksheet_id: int, cell_range=None) -> List[List[str]]:
7879
worksheet = self._get_worksheet(worksheet_id)
7980
return worksheet.get_values(cell_range)
8081

81-
@with_retry()
82+
@with_retry(non_retryable_exceptions=(WorksheetNotFound,))
8283
def get_worksheet_title(self, worksheet_id: int) -> str:
8384
return self._get_worksheet(worksheet_id).title
8485

@@ -97,7 +98,7 @@ def _yyyymmddhhmmss(timezone: str = "Asia/Seoul") -> str:
9798
def _get_spreadsheet(self) -> Spreadsheet:
9899
return self.gs_client.open_by_key(self.spreadsheet_id)
99100

100-
@with_retry()
101+
@with_retry(non_retryable_exceptions=(WorksheetNotFound,))
101102
def _get_worksheet(self, worksheet_id: int) -> Worksheet:
102103
spreadsheet = self._get_spreadsheet()
103104
worksheet = spreadsheet.get_worksheet_by_id(worksheet_id)
@@ -118,7 +119,7 @@ def create_bigchat_sheet(self, title=None) -> Optional[int]:
118119

119120
return worksheet_id
120121

121-
@with_retry()
122+
@with_retry(non_retryable_exceptions=(WorksheetNotFound,))
122123
def delete_row(self, worksheet_id: int, query: str):
123124
"""
124125
XXX: 실제로 삭제할 정보가 없어서 아무 동작을 하지 않아도 에러를 뱉지 않는다.

src/util/utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ def strip_multiline(text, *args, ignore_first_line=True):
5454
return result
5555

5656

57-
def with_retry(max_try_cnt=10, fixed_wait_time_in_sec=3):
57+
def with_retry(max_try_cnt=10, fixed_wait_time_in_sec=3, non_retryable_exceptions=()):
5858
"""
5959
usage: @with_retry(3, 1)
60+
61+
:param non_retryable_exceptions: 재시도해도 성공할 수 없는 예외 타입 튜플. 잡지 않고 즉시 전파한다.
6062
"""
6163

6264
def decorator(func):
@@ -66,6 +68,8 @@ def wrapper(*args, **kwargs):
6668
for attempt in range(max_try_cnt):
6769
try:
6870
return func(*args, **kwargs)
71+
except non_retryable_exceptions:
72+
raise
6973
except Exception as ex:
7074
last_exception = ex
7175
time.sleep(fixed_wait_time_in_sec)

test/handler/bigchat/test_abandon_bigchat.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from test.handler.bigchat.sample_data import create_sample_reaction_removed_event
55

66
from handler.bigchat.abandon_bigchat import AbandonBigchat
7+
from implementation.google_spreadsheet_client import WorksheetNotFound
78
from implementation.member_finder import Member
89
from implementation.slack_client import Message
910

@@ -59,3 +60,45 @@ def test_run(self):
5960
"msg"
6061
]
6162
)
63+
64+
def test_run_with_deleted_sheet_notifies_user(self):
65+
"""시트가 이미 삭제된 경우, 전용 안내 메시지를 보내고 False 를 반환한다."""
66+
event = create_sample_reaction_removed_event("gogo")
67+
mock_slack_client = MagicMock()
68+
mock_slack_client.get_replies.return_value = [
69+
Message(
70+
ts=event["item"]["ts"],
71+
thread_ts="1689429129.825319",
72+
channel="C03SZTDEDK3",
73+
user="U01BN035Y6L",
74+
text="새로운 빅챗, 등록 완료! <https://docs.google.com/spreadsheets/d/1FtKRO4gmlVg-Si0_CHt-tkpVd3LDTXdsoZ0u98MYd0k/edit#gid=161837744|구글스프레드 시트>",
75+
),
76+
]
77+
mock_member_manager = MagicMock()
78+
mock_member_manager.find.return_value = Member(
79+
kor_name="문성혁",
80+
eng_name="Moon Seonghyeok",
81+
email="email",
82+
phone="phone",
83+
school_name_or_company_name="school_name_or_company_name",
84+
)
85+
mock_gs_client = MagicMock()
86+
mock_gs_client.delete_row.side_effect = WorksheetNotFound("161837744")
87+
sut = AbandonBigchat(
88+
event,
89+
"U01BN035Y6L",
90+
"gogo",
91+
mock_slack_client,
92+
mock_member_manager,
93+
mock_gs_client,
94+
)
95+
96+
result = sut.run()
97+
98+
assert result is False
99+
mock_slack_client.send_message.assert_called_once()
100+
assert (
101+
"이 빅챗의 신청 시트를 찾을 수 없어서 등록을 취소하지 못했어"
102+
in mock_slack_client.send_message.call_args.kwargs["msg"]
103+
)
104+
mock_slack_client.send_message_only_visible_to_user.assert_not_called()

test/handler/bigchat/test_join_bigchat.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from test.handler.bigchat.sample_data import create_sample_reaction_added_event
55

66
from handler.bigchat.join_bigchat import JoinBigchat
7+
from implementation.google_spreadsheet_client import WorksheetNotFound
78
from implementation.member_finder import Member
89
from implementation.slack_client import Message
910

@@ -95,6 +96,43 @@ def test_run_with_old_format_sheet_registers_without_buttons(self):
9596
is None
9697
)
9798

99+
def test_run_with_deleted_sheet_notifies_user(self):
100+
"""시트가 이미 삭제된 경우, 전용 안내 메시지를 보내고 False 를 반환한다."""
101+
event = create_sample_reaction_added_event("gogo")
102+
mock_slack_client = MagicMock()
103+
mock_slack_client.get_replies.return_value = [
104+
Message(
105+
ts=event["item"]["ts"],
106+
thread_ts="1689429129.825319",
107+
channel="C03SZTDEDK3",
108+
user="U01BN035Y6L",
109+
text="새로운 빅챗, 등록 완료! <https://docs.google.com/spreadsheets/d/1FtKRO4gmlVg-Si0_CHt-tkpVd3LDTXdsoZ0u98MYd0k/edit#gid=161837744|구글스프레드 시트>",
110+
),
111+
]
112+
mock_gs_client = MagicMock()
113+
mock_gs_client.append_row.side_effect = WorksheetNotFound("161837744")
114+
mock_member_manager = MagicMock()
115+
mock_member_manager.find.return_value = Member(
116+
kor_name="문성혁",
117+
eng_name="Moon Seonghyeok",
118+
email="email",
119+
phone="phone",
120+
school_name_or_company_name="school_name_or_company_name",
121+
)
122+
sut = JoinBigchat(
123+
event, "gogo", mock_slack_client, mock_gs_client, mock_member_manager
124+
)
125+
126+
result = sut.run()
127+
128+
assert result is False
129+
mock_slack_client.send_message.assert_called_once()
130+
assert (
131+
"이 빅챗의 신청 시트를 찾을 수 없어"
132+
in mock_slack_client.send_message.call_args.kwargs["msg"]
133+
)
134+
mock_slack_client.send_message_only_visible_to_user.assert_not_called()
135+
98136
def test_build_calendar_blocks_with_new_format_sheet(self):
99137
event = create_sample_reaction_added_event("gogo")
100138
mock_gs_client = MagicMock()

test/util/test_utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,19 @@ def always_fail():
128128
always_fail()
129129
self.assertEqual(attempts, 10)
130130

131+
def test_with_retry_non_retryable_exception_raises_immediately(self):
132+
attempts = 0
133+
134+
@with_retry(fixed_wait_time_in_sec=0.01, non_retryable_exceptions=(KeyError,))
135+
def always_fail_with_non_retryable():
136+
nonlocal attempts
137+
attempts += 1
138+
raise KeyError("failure")
139+
140+
with self.assertRaises(KeyError):
141+
always_fail_with_non_retryable()
142+
self.assertEqual(attempts, 1)
143+
131144
def test_with_retry_partial_success(self):
132145
attempts = 0
133146

0 commit comments

Comments
 (0)