261 lines
14 KiB
Markdown
261 lines
14 KiB
Markdown
# 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` kullanılır, ancak geo alarmdan farklı olarak her register isteğinde `webhook.json` içindeki tam webhook (`deliveryPolicy` dahil) gönderilir; `IKLIM_WEBHOOK_INITIALIZED` ve `/v1/users/me` kullanılmaz. Backend nokta alarm kaydında webhook ve `deliveryPolicy` zorunlu tutar, hesabı kullanıcıdan kendisi bulur ve hesapta kayıtlı webhook varsa onu yeniden kullanı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).
|
||
- **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 point-alarm-registrations.csv # point-alarm-registrations-YYYYmmdd-HHMMSS.csv
|
||
python3 nowcast-point-page.py --page-number 2 --page-size 50 # yalnızca tek sayfa
|
||
python3 nowcast-point-page.py --account-id 2f04f1b5-1c0a-4c4e-b0a7-0ba6e9b7f2e1 # yalnızca POWER_USER ve üstü
|
||
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
|
||
|
||
```bash
|
||
python3 nowcast-point-get.py idler.txt --output sonuc.csv # registrationId listesi (TXT), CSV çıktı
|
||
python3 nowcast-point-get.py idler.csv --output sonuc.txt # registrationId sütunlu CSV, JSON Lines çıktı
|
||
python3 nowcast-point-get.py alicilar.txt --by recipient --output sonuc.csv # recipientId ile sorgu
|
||
```
|
||
|
||
TXT girdi örneği (`idler.txt`):
|
||
|
||
```text
|
||
# bu satır atlanır
|
||
f7587d9e-2481-4b4c-818d-c8d1946851b7
|
||
57d1e2ba-bf2d-4ef3-b078-523060b2000c
|
||
```
|
||
|
||
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
|
||
|
||
```bash
|
||
cp nowcast-point-alarm-filter.example.json nowcast-point-alarm-filter.json # filtreleri düzenleyin
|
||
python3 nowcast-point-register.py nowcast-point-registrations.example.csv
|
||
python3 nowcast-point-register.py kayitlar.csv --filter-config baska-filtre.json --webhook-config baska-webhook.json
|
||
```
|
||
|
||
Girdi CSV örneği (sonuç sütunları işlem sonunda aynı dosyaya eklenir):
|
||
|
||
```csv
|
||
recipientId;lat;lng;radius
|
||
musteri-001;39.9208;32.8541;1000
|
||
musteri-002;41.0082;28.9784;5000
|
||
```
|
||
|
||
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
|
||
|
||
```bash
|
||
python3 nowcast-point-update.py guncelle.csv --dry-run # gövdeleri gösterir, istek göndermez
|
||
python3 nowcast-point-update.py guncelle.csv # gerçekten günceller
|
||
```
|
||
|
||
Girdi CSV örneği (boş hücre gönderilmez):
|
||
|
||
```csv
|
||
registrationId;recipientId;lat;lng;radius;lightningType;peakCurrent;inCloudHeight;intersectsAffectedPolygon;intersectsCellPolygon;severityThreshold;speedThreshold;precipitationIntensities
|
||
f7587d9e-2481-4b4c-818d-c8d1946851b7;;;;2500;;;;;;HIGH;;LIGHT|MODERATE
|
||
57d1e2ba-bf2d-4ef3-b078-523060b2000c;;40.0;33.0;;PULSE_IN_CLOUD;;;;true;;;
|
||
```
|
||
|
||
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
|
||
|
||
```bash
|
||
python3 nowcast-point-unregister.py silinecekler.txt # yedek alır, SİL onayı ister
|
||
python3 nowcast-point-unregister.py silinecekler.csv --output sonuc.csv # sonuçları dosyaya da yazar
|
||
python3 nowcast-point-unregister.py silinecekler.txt --backup yedek/point-yedek.csv # yedek yolu
|
||
python3 nowcast-point-unregister.py silinecekler.txt --no-backup # yedeksiz (önerilmez)
|
||
```
|
||
|
||
Girdi `registrationId` içeren bir CSV veya satır başına bir ID içeren TXT olabilir.
|
||
|
||
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.
|