Files
iklim-sandbox/README.md
T
mozdemir aaa0c99eb2 fix: stop sending accountId implicitly from the page client
The page client no longer resolves accountId from /v1/users/me. accountId is only sent when passed with --account-id, and a 403 for it now raises a clear error telling the user to omit it because the server scopes the listing to the caller's own account.
2026-10-23 19:31:48 +03:00

210 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# iklim.co Python sandbox istemcileri
Bu klasör HMAC imzalı istek, otomatik login/refresh ve `.env` token saklama
özelliklerini ortak bir istemci katmanında toplar. Geo alarm registration istemcisi
bu ortak yapıyı kullanarak noktalı virgüllü CSV satırlarını sırayla işler.
## Dosyalar
- `common/`: tekrar kullanılabilir API, HMAC, retry ve authentication katmanı
- `login.py`: manuel login komutu
- `nowcast-geo-register.py`: CSV geo alarm registration istemcisi
- `nowcast-point-page.py`, `nowcast-point-get.py`, `nowcast-point-register.py`, `nowcast-point-update.py`, `nowcast-point-unregister.py`: nokta alarm (`/v1/alarms/points`) istemcileri
- `nowcast-point-alarm-filter.example.json`, `nowcast-point-registrations.example.csv`, `nowcast-point-update.example.csv`: nokta alarm şablonları
- `.env.example`: güvenli ortam değişkeni şablonu
- `webhook.example.json`: dört webhook authentication seçeneğini içeren şablon
- `nowcast-geo-alarm-filter.example.json`: ortak nowcast geo alarm filtreleri şablonu
- `nowcast-geo-alarm-registrations.example.csv`: CSV şablonu
Gerçek değerler için `.env`, `webhook.json` ve
`nowcast-geo-alarm-filter.json` kullanılır. `.env` ile `webhook.json` kaynak kod
deposuna eklenmemelidir.
## Kurulum
```bash
cd iklim-sandbox
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
```
Yeni kurulumda şablonları kopyalayın ve gerçek değerlerle düzenleyin:
```bash
cp .env.example .env
cp webhook.example.json webhook.json
cp nowcast-geo-alarm-filter.example.json nowcast-geo-alarm-filter.json
cp nowcast-geo-alarm-registrations.example.csv nowcast-geo-alarm-registrations.csv
```
## CSV biçimi
Girdi UTF-8 ve noktalı virgül ayracına sahip olmalıdır:
```csv
city;district;recipientId
Ankara;Çankaya;recipient-001
İstanbul;Kadıköy;recipient-002
```
Script aynı dosyaya şu üç sonuç sütununu ekler veya mevcut değerlerini günceller:
```csv
city;district;recipientId;status;httpStatus;registrationId
Ankara;Çankaya;recipient-001;SUCCESS;HTTP_200;f7587d9e-2481-4b4c-818d-c8d1946851b7
```
- İl ve ilçe adları trim edilip Türkçe büyük/küçük harf kurallarıyla kesin eşleştirilir.
- Bulunamayan konumlar `FAILED;NOT_SENT;` olarak kaydedilir.
- Daha önce `SUCCESS` olmuş satırlar konsola bilgi yazılarak atlanır.
- Her satırdan sonra CSV aynı dizinde geçici dosyayla atomik olarak güncellenir.
- `recipientId` boş olamaz ve US-ASCII karakterlerinden oluşmalıdır.
## İl ve ilçe sorgu cache'i
Şehir listesi her script çalıştırmasında yalnızca bir kez API'den alınır ve isim-ID
eşleştirmesi bellekte tutulur. İlçe listesi de her benzersiz şehir için yalnızca ilk
karşılaşmada alınır; aynı şehirdeki sonraki CSV satırlarında bellekteki sonuç
kullanılır. Cache diske yazılmaz ve script kapandığında temizlenir.
## Webhook yapılandırması
`webhook.json` içindeki `authentication.selected` şu değerlerden biri olmalıdır:
- `BASIC`
- `JWT_TOKEN`
- `API_KEY`
- `HMAC_SIGNATURE`
Yalnızca seçilen seçeneğin `options` altındaki alanları API'ye gönderilir. İlk
initialization isteğinde tam webhook config gönderilir ve `accountId` kesinlikle
gönderilmez. İlk başarılı kayıttan sonra `.env` içindeki
`IKLIM_WEBHOOK_INITIALIZED=true` yapılır. Sonraki kayıtlar webhook altında yalnızca
API'nin `/v1/users/me` yanıtındaki `account.id` değerini gönderir. Webhook'u yeniden
tanımlamak için bu değeri elle `false` yapın.
Test/prod ortamını veya kullanıcı hesabını değiştirirken de yeni ortamın webhook'u
ilk kayıtla tanımlayabilmesi için `IKLIM_WEBHOOK_INITIALIZED=false` yapın.
## Nowcast geo alarm filter yapılandırması
Tüm CSV satırlarına uygulanacak ortak nowcast filtreleri
`nowcast-geo-alarm-filter.json` içinde tutulur. Yıldırım, thunderstorm ve yağış
filtrelerinden ihtiyaç duyulmayan nesneler dosyadan çıkarılabilir. API isteğindeki
alan adı doküman gereği yine `filter` olarak gönderilir.
## Otomatik authentication
Ortak istemci her yetkili istekten önce access token'ı kontrol eder:
1. Access token yoksa `.env` kullanıcı bilgileriyle login olur.
2. Access token'ın bitmesine 60 saniyeden az kaldıysa geçerli refresh token ile yeniler.
3. Refresh token yoksa/geçersizse yeniden login olur.
4. Sunucu `401` döndürürse refresh veya login yapıp isteği bir kez daha gönderir.
5. Yeni tokenları ve JWT `exp` değerlerini `.env` dosyasına kaydeder.
Yetkili isteklerde `Authorization: Bearer <accessToken>` ile HMAC başlıkları birlikte
gönderilir. Tokenlar ve parolalar konsola yazılmaz.
## Retry davranışı
Ağ hataları ile `429`, `500`, `502`, `503` ve `504` yanıtları ilk istekten sonra en
fazla üç kez, varsayılan olarak 2, 4 ve 8 saniye beklenerek tekrar edilir. `429`
yanıtındaki `Retry-After` varsa ona uyulur. Bir operasyonun retry isteklerinde aynı
idempotency key; yeni timestamp, nonce ve HMAC imzası kullanılır.
Belirsiz sonuç veya `409` sonrasında kayıt `recipientId` ile sorgulanır. Kayıt
bulunursa `registrationId` kurtarılır ve satır `SUCCESS` yapılır.
## Debug HTTP logları
Her çalıştırmada maskelenmiş HTTP istek ve yanıtları aşağıdaki dosyaya yazılır:
```text
logs/iklim-api-debug.log
```
Log; istek metodu, URL, deneme numarası, başlıklar, istek gövdesi, HTTP durum kodu,
yanıt başlıkları ve yanıt gövdesini içerir. Parola, kullanıcı adı, JWT, refresh
token, API key, webhook secret, `Authorization`, cookie ve `X-Signature` değerleri
maskelenir. Dosya 5 MB'a ulaştığında döndürülür ve en fazla üç eski dosya tutulur.
Başarılı API yanıtındaki `warning`/`warnings` alanları ayrıca konsola yazılır.
## Çalıştırma
Manuel login:
```bash
python login.py
```
CSV registration:
```bash
python nowcast-geo-register.py nowcast-geo-alarm-registrations.csv
```
Farklı config dosyaları vermek için:
```bash
python nowcast-geo-register.py nowcast-geo-alarm-registrations.csv \
--webhook-config another-webhook.json \
--nowcast-filter-config another-nowcast-geo-alarm-filter.json \
--debug-log logs/custom-debug.log
```
## Nokta alarm (nowcast point) istemcileri
Nokta alarm istemcileri `BE-NowcastPointAlarm` servisinin `/v1/alarms/points` endpoint'lerini çağırır. Tüm komutlarda `--help` seçeneklerin tam dökümünü verir. Gerçek filtre değerleri için `nowcast-point-alarm-filter.example.json` dosyasını `nowcast-point-alarm-filter.json` olarak kopyalayın (git'e eklenmez). Webhook için mevcut `webhook.json` ve `IKLIM_WEBHOOK_INITIALIZED` mekanizması aynen kullanılır.
| Komut | Ne yapar |
| --- | --- |
| `nowcast-point-page.py` | Kayıtları sayfa sayfa listeler; SQLite'a (`iklim-sandbox.db`, `registrationId` anahtarlı) yazar, ekrana basar veya `--output` ile CSV/TXT üretir |
| `nowcast-point-get.py` | CSV/TXT içindeki `registrationId` (`--by id`) veya `recipientId` (`--by recipient`) değerlerini sorgular |
| `nowcast-point-register.py` | CSV satırlarından kayıt oluşturur |
| `nowcast-point-update.py` | CSV satırlarıyla kayıtları kısmen günceller (`--dry-run` destekler) |
| `nowcast-point-unregister.py` | Listedeki kayıtları siler (onay ve yedekle) |
### Önemli uyarılar
- **Page endpoint'inde sıralama (ORDER BY) yoktur.** Tüm sayfalar gezilirken kayıtların sırası garanti değildir; bu yüzden sayfa kayması yaşanabilir (bir kayıt iki kez gelebilir, bir kayıt hiç gelmeyebilir). Backend tarafında `createdAt` ardından `id` ile sıralama önerildi (yeni kayıtlar sona eklenir, `id` eşit zamanlarda sırayı sabitler).
- **Tüm kayıtlar çekilirken aynı anda register, update veya unregister yapmayın.** Veri değişirse sayfalar kayar. `nowcast-point-page.py` sonunda sunucunun bildirdiği `totalRecords` ile alınan benzersiz kayıt sayısını karşılaştırır ve fark varsa uyarı verir; bu durumda komutu yeniden çalıştırın. SQLite `registrationId` ile upsert yaptığı için tekrar çalıştırmak kayıt çoğaltmaz.
- `nowcast-point-page.py` varsayılan olarak `accountId` göndermez; sunucu listeyi çağıranın kendi hesabına daraltır ve normal kullanıcılar için doğru kullanım budur. `--account-id` yalnızca en az POWER_USER yetkisiyle verilebilir; aksi halde HTTP 403 döner ve betik `--account-id` vermemenizi söyleyen bir hata yazar. ADMIN kullanıcı `accountId` vermezse tüm hesapları görür.
- Backend her `RuntimeException`'ı HTTP 400 yapar; kayıt bulunamaması da 400 döner. Yalnızca gerçek 404 `NOT_FOUND` olarak işaretlenir.
### page
```bash
python3 nowcast-point-page.py # tüm sayfalar, ekrana
python3 nowcast-point-page.py --output kayitlar.csv # kayitlar-YYYYmmdd-HHMMSS.csv
python3 nowcast-point-page.py --page-number 2 --page-size 50 # yalnızca tek sayfa
python3 nowcast-point-page.py --recipient-id musteri-1 --recipient-id musteri-2
python3 nowcast-point-page.py --polygon "32.8,39.9;32.9,39.9;32.9,40.0;32.8,39.9"
```
- Poligon `lng,lat` çiftlerinden oluşan **düz halkadır**, en az 4 nokta ve kapalı olmalıdır. Filtre, kayıt noktasının poligonla kesişimine bakar; yarıçap yok sayılır.
- Çıktıda webhook bilgisi dosya başında bir kez yer alır (CSV'de `#webhook` satırı); `password`, `jwtToken`, `apiKey` ve `secret` alanları `***` ile maskelenir. Her filtre ayrı bir sütundur. API yanıtında `createdAt`/`updatedAt` bulunmadığı için çıktıda da yoktur.
- Etkileşimli terminalde ve `--output` verilmediğinde sonunda "SQLite verisi CSV/TXT dosyasına aktarılsın mı?" sorulur.
### get
Girdi CSV ise başlıkta `registrationId` (veya `recipientId`) sütunu aranır; TXT ise satır başına bir ID yazılır, boş satırlar ve `#` ile başlayanlar atlanır. `--output` zorunludur; uzantı `.csv` ise noktalı virgüllü CSV, `.txt` ise JSON Lines yazılır. Satır durumları `SUCCESS`, `NOT_FOUND` (HTTP 404) ve `FAILED`'dır (`httpStatus` ve `errorBody` dolar). Bir `recipientId` birden çok kayıt dönerse her kayıt ayrı satırdır.
### register
CSV sütunları yalnızca `recipientId;lat;lng;radius` değerleridir (`radius` metre, 0-50000). Filtre CSV'de tekrarlanmaz; ortak `nowcast-point-alarm-filter.json` dosyasından okunur ve her kayda uygulanır (`--filter-config` ile değiştirilir). Sonuç sütunları aynı dosyaya yazılır: `status;httpStatus;registrationId;errorBody`. HTTP 200 dışındaki yanıtlarda cevap gövdesi `errorBody` içine yazılır. `SUCCESS` satırlar yeniden çalıştırmada atlanır. Sonucu belirsiz kalan istek yalnızca `recipientId`, nokta ve yarıçap birebir eşleşirse mevcut kayıtla kurtarılır. Aynı hesap, `recipientId`, nokta ve yarıçap ikinci kez kaydedilemez (400).
### update
CSV'de `registrationId` zorunludur; `recipientId;lat;lng;radius` ve filtre sütunları (`lightningType;peakCurrent;inCloudHeight;intersectsAffectedPolygon;intersectsCellPolygon;severityThreshold;speedThreshold;precipitationIntensities`, liste `|` ile ayrılır) isteğe bağlıdır. **Boş hücre gönderilmez**; `lat` ve `lng` birlikte verilmelidir. Önce `--dry-run` ile gövdeleri kontrol edin.
- Backend PATCH'i bir alanı **temizleyemez** (null = dokunma).
- `thunderstorm.speedThreshold` backend PATCH'inde işlenmiyor, değer gönderilse de güncellenmeyebilir (GAIA-476).
- Backend sınır güncellemesinde doğrulama yapmaz; yarıçap ve koordinat aralıkları istemcide yerelde doğrulanır.
- Webhook güncellemesi bu sürümde desteklenmez.
### unregister
Başlamadan önce ortam adı ve URL, silinecek kayıt sayısı ve "geri alınamaz" uyarısı gösterilir; bir hesabın **son kaydı** silinirse webhook tanımı da silinir. Onay için `SİL` yazılmalıdır (`--yes` yoktur). `prod` ortamında ek olarak ortam adı (`prod`) yazdırılır. Silmeden önce her kayıt `get-by-id` ile yedeklenir (`--backup`, kapatmak için `--no-backup`); yedek yazılamazsa hiçbir kayıt silinmez.