feat: add nowcast point alarm unregister client with confirmation and backup

Shows environment, count and an irreversibility warning, requires typing the confirmation word (plus the environment name on prod), backs every registration up through get-by-id before deleting and aborts the whole run if the backup cannot be completed.
This commit is contained in:
2026-10-23 19:14:08 +03:00
parent 8587669ea5
commit 4d270aea05
4 changed files with 208 additions and 0 deletions
+75
View File
@@ -943,3 +943,78 @@ class PointUpdateProcessor:
row["status"] = status
row["httpStatus"] = http_status
row["errorBody"] = error_body
CONFIRM_WORD = "SİL"
class PointUnregisterProcessor:
"""Delete registrations listed in a CSV/TXT after a typed confirmation and a backup."""
def __init__(
self,
service: PointAlarmService,
*,
ids: list[str],
environment: str,
base_url: str,
backup_path: Path | None,
input_fn: Callable[[str], str] = input,
out: TextIO | None = None,
) -> None:
self.service = service
self.ids = ids
self.environment = environment
self.base_url = base_url
self.backup_path = backup_path
self.input_fn = input_fn
self.out = out or sys.stdout
def confirm(self) -> bool:
say = lambda text: self.out.write(text + "\n")
say(f"Ortam: {self.environment or '-'} ({self.base_url})")
say(f"Silinecek kayıt sayısı: {len(self.ids)}")
say("Bu işlem geri alınamaz. Bir hesabın SON kaydı silinirse hesabın webhook tanımı da silinir.")
if self.environment == "prod":
if self.input_fn("PROD ortamında işlem yapıyorsunuz. Onaylamak için 'prod' yazın: ").strip() != "prod":
say("Ortam adı eşleşmedi; işlem iptal edildi.")
return False
if self.input_fn(f"Devam etmek için '{CONFIRM_WORD}' yazın: ").strip() != CONFIRM_WORD:
say("Onay verilmedi; işlem iptal edildi.")
return False
return True
def run(self) -> list[dict[str, Any]]:
if not self.confirm():
return []
backups: list[dict[str, Any]] = []
results: list[dict[str, Any]] = []
webhooks: list[dict[str, Any]] = []
if self.backup_path:
for registration_id in self.ids:
try:
data = self.service.get_by_id(registration_id).data
except ApiError as exc:
if exc.status_code == 404:
LOGGER.warning("%s bulunamadı; yedeklenemedi.", registration_id)
continue
# An unreadable record cannot be backed up, so nothing is deleted.
raise
if isinstance(data, dict):
backups.append(flatten_registration(data))
webhook = registration_webhook(data)
if webhook and webhook not in webhooks:
webhooks.append(webhook)
# A backup that cannot be written must stop the run before anything is deleted.
write_records(self.backup_path, backups, webhooks)
LOGGER.info("Yedek yazıldı: %s (%s kayıt)", self.backup_path, len(backups))
for registration_id in self.ids:
try:
response = self.service.unregister(registration_id)
except ApiError as exc:
results.append({"registrationId": registration_id, "status": "FAILED", "httpStatus": exc.status_code, "errorBody": error_body_text(exc)})
LOGGER.error("%s silinemedi: %s", registration_id, exc)
else:
results.append({"registrationId": registration_id, "status": "SUCCESS", "httpStatus": response.status_code, "errorBody": ""})
LOGGER.info("%s silindi.", registration_id)
return results