OCPP Faz 3 — REST API Design Decisions
Bu doküman, Zeus 2.0 OCPP entegrasyonu Faz 3 (REST API + business services + provisioning kit) öncesi kesinleştirilen API kontrat kararlarını ve gerekçelerini belgeler. Karar verilen değerler Faz 3 implementasyonu boyunca bağlayıcıdır; değişiklikler Changelog ile duyurulur.
Onay tarihi: 2026-04-25
Onaylayan: gucluceyhan
Önceki fazlar: Saha Entegrasyon Kılavuzu, Faz 1+2 commit a99c55c
1. URL Versioning ve Yapısı
Karar
- Base path:
/api/ocpp/(versiyonsuz) - Çoklu kaynaklar plural noun:
chargers,sessions,reservations,charging-profiles - Path segment'ler kebab-case:
remote-start,change-availability,local-auth-list - Komut RPC'ler:
POST /api/ocpp/chargers/{id}/commands/<action>
Gerekçe
Zeus mevcut convention'ı karışık (çoğunluk versiyonsuz; Zigbee/Templates/BMS-Identity v1). Yeni feature için versiyonsuz standart, çoğunluk pattern'i ile uyumlu. İleride breaking değişiklik gerekirse /api/v2/ocpp/ ile coexist edilir + Deprecation header.
Örnekler
GET /api/ocpp/chargers
GET /api/ocpp/chargers/{id}
POST /api/ocpp/chargers
PATCH /api/ocpp/chargers/{id}
DELETE /api/ocpp/chargers/{id}
POST /api/ocpp/chargers/{id}/commands/remote-start
POST /api/ocpp/chargers/{id}/commands/remote-stop
POST /api/ocpp/chargers/{id}/commands/reset
POST /api/ocpp/chargers/{id}/commands/unlock
POST /api/ocpp/chargers/{id}/commands/change-availability
POST /api/ocpp/chargers/{id}/commands/change-config
POST /api/ocpp/chargers/{id}/commands/trigger-message
POST /api/ocpp/chargers/{id}/commands/update-firmware
GET /api/ocpp/chargers/{id}/charging-profiles
POST /api/ocpp/chargers/{id}/charging-profiles
GET /api/ocpp/chargers/{id}/charging-profiles/{profile_id}
DELETE /api/ocpp/chargers/{id}/charging-profiles/{profile_id}
GET /api/ocpp/chargers/{id}/local-auth-list
PUT /api/ocpp/chargers/{id}/local-auth-list
POST /api/ocpp/chargers/{id}/reservations
GET /api/ocpp/chargers/{id}/reservations # PR-E5
DELETE /api/ocpp/chargers/{id}/reservations/{reservation_id}
GET /api/ocpp/chargers/{id}/command-logs # PR-E5
GET /api/ocpp/auth-list/tenant # PR-E4
POST /api/ocpp/auth-list/tenant # PR-E4
PATCH /api/ocpp/auth-list/tenant/{id_tag} # PR-E4
DELETE /api/ocpp/auth-list/tenant/{id_tag} # PR-E4
POST /api/ocpp/auth-list/tenant/push # PR-E4
GET /api/ocpp/sessions
GET /api/ocpp/sessions/{id}
GET /api/ocpp/sessions/{id}/meter-samples
GET /api/ocpp/sessions/export.csv
POST /api/ocpp/chargers/{id}/rotate-password
GET /api/ocpp/chargers/{id}/provisioning-kit.pdf
Yeni endpoint'ler (PR-E2 → PR-E5, 2026-04-27/28)
| Endpoint | PR | Açıklama |
|---|---|---|
GET /chargers?subregion_id={uuid} | PR-E2 (#287) | subregion_id query parametre + response subregion_id / subregion_name field'ları. |
GET /chargers/{id}/reservations | PR-E5 (#290) | Rezervasyon listesi (CancelReservation dropdown). status filter `active |
GET /chargers/{id}/command-logs | PR-E5 (#290) | Son OCPP komutlarının istek/yanıt audit trail'i (created_at DESC). Default limit 20, max 100. Permission: READ_CHARGER. |
GET /auth-list/tenant | PR-E4 (#289) | Tenant-wide RFID kart listesi (charger_id IS NULL). Permission: READ_CHARGER. |
POST /auth-list/tenant | PR-E4 (#289) | Yeni RFID kart. 409 OCPP_AUTH_LIST_DUPLICATE → id_tag tenant'ta zaten var. Permission: CONFIGURE_CHARGER. |
PATCH /auth-list/tenant/{id_tag} | PR-E4 (#289) | Status / expiry_date / parent_id_tag güncelle. Permission: CONFIGURE_CHARGER. |
DELETE /auth-list/tenant/{id_tag} | PR-E4 (#289) | Kart sil. Permission: CONFIGURE_CHARGER. |
POST /auth-list/tenant/push | PR-E4 (#289) | Seçili charger'lara differential SendLocalList push. Response: per-charger `accepted |
GET /chargers/{id}/command-logs — komut audit trail
Query params: limit (default 20, max 100), offset (default 0).
Response: PaginatedResponse[CommandLogResponse]
{
"items": [
{
"id": "uuid",
"charger_id": "uuid",
"action": "Reset",
"direction": "outbound",
"request_payload": {"type": "Soft"},
"response_payload": {"status": "Accepted"},
"result": "accepted",
"error_code": null,
"error_message": null,
"request_id": "req_2k4j8h",
"actor_user_id": "uuid",
"latency_ms": 1450,
"created_at": "2026-04-28T10:15:30Z"
}
],
"total": 42,
"limit": 20,
"offset": 0
}
Saha senaryosu: Reset gönderildi, charger Rejected döndü — kullanıcı UI'da sonucu göremedi. Bu endpoint UI'da CommandLogsPanel.tsx ile son 20 komutu inceleme imkanı verir.
GET /chargers/{id}/reservations — rezervasyon listesi
Query params:
status:active | used | cancelled | expired(PydanticLiteralwhitelist; geçersiz değer 422). Defaultactive.limit(default 50, max 200),offset(default 0).
Response: PaginatedResponse[ReservationResponse]
{
"items": [
{
"id": "uuid",
"charger_id": "uuid",
"connector_id": 1,
"reservation_id": 42,
"id_tag": "DE*ABC*1234",
"parent_id_tag": null,
"expiry_date": "2026-04-28T16:00:00Z",
"status": "active",
"created_at": "2026-04-28T10:00:00Z"
}
],
"total": 3,
"limit": 50,
"offset": 0
}
/auth-list/tenant family — tenant-wide RFID kart yönetimi
Mimari notu: Authorize handler _lookup_id_tag charger-spesifik (/chargers/{id}/local-auth-list) → tenant-wide fallback yapar. Tenant-wide kayıtlar charger_id IS NULL ile saklanır; aynı id_tag charger-spesifik override için kopyalanabilir.
POST /auth-list/tenant — örnek request:
{
"id_tag": "DE*ABC*1234",
"status": "Accepted",
"expiry_date": "2027-01-01T00:00:00Z",
"parent_id_tag": null
}
POST /auth-list/tenant/push — örnek request:
{
"charger_ids": ["uuid-a", "uuid-b", "uuid-c"]
}
Response:
{
"pushed_at": "2026-04-28T10:15:30Z",
"total": 3,
"results": [
{"charger_id": "uuid-a", "result": "accepted", "list_version": 7},
{"charger_id": "uuid-b", "result": "offline", "error": "OCPP_CHARGER_OFFLINE"},
{"charger_id": "uuid-c", "result": "rejected", "error": "VersionMismatch"}
]
}
GET /chargers?subregion_id={uuid} — subregion filter (PR-E2)
OcppChargerListItem response'a 2 yeni opsiyonel field eklendi (non-breaking):
subregion_id: UUID | nullsubregion_name: string | null
Frontend chargers/page.tsx filter chip ve charger formundaki region → subregion cascade select bu field'ları kullanır.
2. HTTP Method ve OCPP Status Code Mapping
Karar
OCPP komut yanıtları aşağıdaki HTTP status code'larına map edilir:
| OCPP Sonuç | HTTP Status | Yanıt Body |
|---|---|---|
Accepted | 200 OK | {"status": "accepted", ...} + komut-spesifik field'lar |
Rejected | 409 Conflict | error_code: OCPP_REMOTE_REJECTED |
NotImplemented (charger desteklemiyor) | 501 Not Implemented | error_code: OCPP_NOT_IMPLEMENTED |
NotSupported | 501 Not Implemented | error_code: OCPP_NOT_SUPPORTED |
| Charger offline | 503 Service Unavailable | error_code: OCPP_CHARGER_OFFLINE |
| Dispatcher timeout (30 sn) | 504 Gateway Timeout | error_code: OCPP_COMMAND_TIMEOUT |
| Validation hatası | 400 Bad Request | error_code: VALIDATION_ERROR, details: {...} |
| Yetki yok | 403 Forbidden | error_code: PERMISSION_DENIED |
| Charger bulunamadı | 404 Not Found | error_code: NOT_FOUND |
| Idempotency replay | 200 OK | Cached yanıt + X-Idempotency-Replay: true header |
Gerekçe
Rejectedcharger'ın iş kurallarınca reddi (örn. konnektör meşgul) → 409 (kaynak çatışması) semantik olarak doğru.NotImplementedHTTP 501 RFC 7231'e uygun (server fonksiyonu desteklemiyor).- Charger offline 503 (geçici, retry mantıklı) — 502 değil (502 upstream'in arızası anlamına gelir).
- Dispatcher timeout 504 (upstream gateway timeout — bizim için "upstream" charger).
3. Error Envelope Standardı
Karar
Canonical envelope (Zeus genelinde OCPP pilot olarak kullanılır):
{
"error_code": "OCPP_REMOTE_REJECTED",
"message": "Charger remote start isteğini reddetti",
"details": {
"charger_id": "...",
"connector_id": 1,
"ocpp_status": "Rejected"
},
"request_id": "req_2k4j8h2k4j8h"
}
Field'lar
error_code(zorunlu, snake_case-UPPER veyaOCPP_*prefixli): Programatik karşılama için.message(zorunlu, Türkçe): Kullanıcıya gösterilebilir.details(opsiyonel, dict): Ek bağlam (charger_id, validation field'ları, OCPP raw status).request_id(zorunlu): UUID hex; loglarda ve metric'lerde aynı ID ile takip; trace correlation.
Gerekçe
Zeus mevcut iki envelope kullanıyor ({detail: ...} FastAPI default + {error: ...} custom handler). OCPP yeni feature olduğu için canonical envelope pilot. request_id debug için kritik — incident sırasında "şu request başarısız mıydı?" sorusu cevaplanabilir.
Error Code Taxonomy (ön-tanımlı)
VALIDATION_ERROR— Pydantic field hataları (Issue #502:RemoteStartRequestmutexid_tag/parent_id_tagXOR ihlali dahil)PERMISSION_DENIED— RBAC reddiNOT_FOUND— Kaynak yokOCPP_REMOTE_REJECTED— Charger RejectedOCPP_NOT_IMPLEMENTED— Charger NotImplementedOCPP_NOT_SUPPORTED— Charger NotSupportedOCPP_CHARGER_OFFLINE— Charger WS bağlı değilOCPP_COMMAND_TIMEOUT— Dispatcher 30sn timeoutOCPP_INVALID_DOWNSAMPLE— Downsample formatı geçersizOCPP_INVALID_PROFILE_SCHEDULE— SmartCharging schedule schema reddiOCPP_DUPLICATE_REQUEST— Idempotency window içinde tekrar (200 dönmeden, edge case)OCPP_AUTH_LIST_DUPLICATE(PR-E4) —POST /auth-list/tenanttenant'ta aynı id_tag mevcut (409)OCPP_PARENT_TAG_NO_ACTIVE_CARD(Issue #502, 404) —RemoteStartTransactionçağrısında verilenparent_id_tagiçin tenant altında aktif kart yok (status='Accepted'veexpiry_dategelecekte). Operator UI'da "Tanımlı aktif kart yok" mesajı görür veRFID Kartlarıekranına yönlendirilir.OCPP_PARENT_TAG_DATA_INTEGRITY(Issue #502, 500) — 1:1 invariant ihlali: aynıparent_id_tagaltında 2+ aktif kart. BackendLIMIT 2ile yakalar; Migration 0056 partial UNIQUE INDEX (uq_auth_list_parent_per_tenant) prod'a indikten sonra DB-level fail-safe devreye girer. Operasyon ekibidocs/runbook/0056_parent_id_tag_cleanup.mdrunbook'undaki adımlarla duplicate kayıtları temizler.
Yeni error_code eklemeleri Changelog'a yazılır.
4. Permission Modeli
Karar
5'li granülerlik (mevcut MANAGE_DEVICE/READ_DEVICE pattern'iyle paralel):
| Permission | Kapsam |
|---|---|
READ_CHARGER | Liste + detay + session geçmişi + meter samples + rapor |
WRITE_CHARGER | Charger CRUD (create/update/delete) — iş tarafı meta verileri |
CONTROL_CHARGER | Komut RPC'leri (RemoteStart/Stop, Reset, Unlock, ChangeAvailability) |
CONFIGURE_CHARGER | ChangeConfiguration, SetChargingProfile, SendLocalList, ReserveNow |
OTA_CHARGER | UpdateFirmware, GetDiagnostics |
MANAGE_CHARGER_SECRETS | Password rotate + provisioning-kit.pdf indirme |
Rol Şablonları (öneri)
| Rol | Permission'lar |
|---|---|
admin | Hepsi |
operator | READ_CHARGER, CONTROL_CHARGER, CONFIGURE_CHARGER (saha teknisyeni) |
viewer | READ_CHARGER (raporlama, dashboard) |
installer | READ_CHARGER, WRITE_CHARGER, MANAGE_CHARGER_SECRETS (ilk kurulum) |
Gerekçe
Tek MANAGE_CHARGER çok geniş — RemoteStart vs ChangeConfiguration vs UpdateFirmware vs password rotate çok farklı yetki seviyeleri gerektirir. Production'da:
- Saha teknisyeni komutları çalıştırabilmeli ama firmware push edememeli (OTA ayrı yetki).
- Stok yöneticisi charger ekleyebilmeli ama ChargingProfile yazamamalı.
- Password rotate ve provisioning-kit.pdf çok hassas → ayrı yetki.
5. Idempotency
Karar
Komut RPC endpoint'lerinde idempotency (POST /commands/*):
- Opsiyonel header:
Idempotency-Key: <client-uuid> - Server-side dedup window: Header gönderilmemişse, server
(charger_id, action, connector_id, id_tag)tuple üzerinde 10 saniyelik dedup. - Cache TTL: Header gönderilmişse, server bu key için 24 saat boyunca yanıtı cache'ler.
- Replay yanıtı: Aynı key ile gelen ikinci istek → HTTP 200 + ilk yanıtın aynısı +
X-Idempotency-Replay: trueheader.
Storage
Redis: ocpp:idempotency:{tenant_id}:{key_hash} → JSON-encoded response, TTL 24h. Sadece başarılı yanıtlar (2xx) cache'lenir; 4xx/5xx hatalar cache'lenmez (retry yapılabilsin).
Gerekçe
RemoteStart double-click → frontend timeout retry → çift transaction açılma riski. OCPP 1.6J spec native idempotency içermez; uygulama katmanı korumalı. Idempotency-Key header pattern'i Stripe/Square gibi ödeme sistemlerinde kanıtlanmış.
İstemci Önerisi
Frontend her komut çağrısında Idempotency-Key: ${crypto.randomUUID()} üretir; retry'ları aynı key ile yapar.
6. Pagination ve Filtering
Karar
Offset-based pagination (Zeus mevcut devices endpoint convention'ı):
| Parametre | Default | Max |
|---|---|---|
limit | 50 | 200 |
offset | 0 | (sınırsız ama TimescaleDB hypertable için 10000+ uyarı) |
Filtreler (her endpoint'in semantiği):
GET /api/ocpp/chargers?status=online&tenant_subregion_id=...
GET /api/ocpp/sessions?charger_id=...&from=2026-01-01T00:00:00Z&to=2026-01-31T23:59:59Z&status=completed
GET /api/ocpp/sessions/{id}/meter-samples?measurand=Energy.Active.Import.Register&downsample=1m
Time range filter:
fromvetoISO 8601 UTC (örn.2026-01-01T00:00:00Z); naive datetime reddedilir (400).- Default
to=now(), defaultfrom=to - 24h. from > to→ 400VALIDATION_ERROR.
Response Pagination Metadata
{
"items": [],
"total": 12345,
"limit": 50,
"offset": 0
}
Gerekçe
Cursor-based daha verimli ama Zeus convention offset; tutarlılık için aynı pattern.
7. Time Series Downsampling
Karar
downsample query parametresi — meter-samples ve telemetri endpoint'lerinde:
- Format: PromQL-style — regex
^[0-9]+(s|m|h|d)$ - Geçerli değerler:
30s,1m,5m,15m,1h,4h,1d - Default: Yok (kullanıcı belirtmezse raw samples; ancak max 10 000 row cap, aşılırsa 400
OCPP_INVALID_DOWNSAMPLEdöner ve mesaj "explicit downsample gerekli" olur) - Validation error:
OCPP_INVALID_DOWNSAMPLE400
Backend Mapping
DOWNSAMPLE_TO_INTERVAL = {
"s": "second", "m": "minute", "h": "hour", "d": "day",
}
# "1m" -> time_bucket(INTERVAL '1 minute', time)
TimescaleDB time_bucket() fonksiyonu kullanılır; charger × measurand bazında ortalama (avg) ve son değer (last(value, time)) döndürülebilir (endpoint parametresine göre).
Gerekçe
PromQL-style kompakt + URL kısalığı; Grafana ve diğer monitoring araçlarıyla uyumlu. ISO 8601 (PT1M) okunabilir değil; 1minute PostgreSQL native ama URL'de uzun.
PR-B (2026-05-11) — phase alanı response normalize semantiği
GET /api/ocpp/sessions/{id}/meter-samples response'unda her sample item'ın phase alanı:
- Tip:
string | null(Pydantic schema:MeterSampleResponse.phase: str | None). - Değerler:
"L1" | "L2" | "L3" | "L1-N" | "L2-N" | "L3-N" | "N" | null(OCPP 1.6 spec: 7 enum value + opsiyonel). nullliteral anlamı: Phase belirtilmedi (vendor tek-faz cihaz vephasealanını gönderir/göndermez fark etmez).
DB seviyesinde sentinel: Migration 0042 sonrası ocpp_meter_samples.phase kolonu NOT NULL DEFAULT ''. Vendor phase atlarsa backend '' literal'i yazar (PK 4-tuple içinde; eski 3-tuple PK'de NULL satırlar IntegrityError atıyordu). Backend response layer (service.py:982 raw + :1023 downsample) '' sentinel'i frontend'e null olarak normalize eder. Bu, mevcut frontend LiveMeterChart preferL1 ['L1','L2','L3', null, undefined] davranışını korur.
Geriye dönük uyumlu: API contract değişmedi; frontend kod değişikliği gerektirmedi. Detay: PR-B Changelog Entry.
8. Sessions Endpoint Yapısı
Karar
Flat:
GET /api/ocpp/sessions?charger_id=...&from=...&to=...&status=...
GET /api/ocpp/sessions/{id}
GET /api/ocpp/sessions/{id}/meter-samples?...
Nested (/chargers/{id}/sessions) YAZILMAZ.
Gerekçe
- Orphan-tolerant: Charger silinse bile session geçmişi raporlama/billing için sorgulanmalı (FK
RESTRICTile zaten silinemez ama soft scenario için). - Filter esnekliği: Tek endpoint hem charger-bazında, hem tarih-bazında, hem multi-charger sorgularına uyumlu.
- Tek source of truth: İki endpoint olsa frontend'de hangisini kullanacağı belirsiz.
9. Charging Profiles Path Yapısı
Karar
4 ayrı endpoint (overloaded path YAZILMAZ):
GET /api/ocpp/chargers/{id}/charging-profiles # liste
POST /api/ocpp/chargers/{id}/charging-profiles # olustur + push
GET /api/ocpp/chargers/{id}/charging-profiles/{profile_id} # detay
DELETE /api/ocpp/chargers/{id}/charging-profiles/{profile_id} # sil + ClearChargingProfile
PUT /api/ocpp/chargers/{id}/charging-profiles/{profile_id} YAZILMAZ — OCPP SetChargingProfile (replace semantik) zaten POST + UNIQUE (charger_id, connector_id, stack_level, profile_purpose) constraint ile aynı stack'i replace eder.
POST Davranışı
- Pydantic schema validation (Zod-equivalent —
chargingProfileKind×recurrencyKindkombinasyonları,chargingSchedulePeriods[]non-empty). - DB INSERT (status='draft').
- Dispatcher
SetChargingProfilepush. - Charger
Accepted→ status='pushed' → 200. - Charger
Rejected→ status='rejected' → 409 +error_code: OCPP_INVALID_PROFILE_SCHEDULE+details.ocpp_reason.
Gerekçe
OpenAPI'da [/{profile_id}] notation geçerli değil; FastAPI router'da iki ayrı handler olur. Net path = net dokümantasyon.
10. Authentication ve Authorization
Karar
- Tüm REST endpoint'ler: JWT zorunlu (FastAPI
Depends(get_current_user)). - Tenant isolation: Her endpoint'te
current_user.tenant_idfilter zorunlu (CLAUDE.md §4 kuralı). - Permission check: Endpoint başında
Depends(require_permissions([Permission.X])). - WS endpoint (
/ocpp/1.6/{cpid}): HTTP Basic (Faz 2 mevcut; charger-side auth, kullanıcı/admin değil).
REST → Charger Auth Köprüsü
REST endpoint kullanıcı tenant'ında bir charger'a komut gönderirken:
- JWT'den tenant_id çıkarılır.
- Charger DB lookup
WHERE id = :charger_id AND tenant_id = :tenant_id(tenant izolasyonu). - Yoksa 404
NOT_FOUND(cross-tenant probe enumeration koruması — 403 yerine 404). - Varsa dispatcher'a delegate.
11. WebSocket vs REST Ayrımı
Karar
Zeus 3 farklı WebSocket kanalı kullanır; net ayrım:
| Kanal | Yön | Amaç | Kullanıcı |
|---|---|---|---|
/ocpp/1.6/{cpid} | charger ↔ backend | OCPP protokolü | Sadece charger cihazları |
/ws/telemetry | backend → frontend | Live event push (status, meter, command results) | Frontend |
REST /api/ocpp/* | frontend ↔ backend | CRUD + komut RPC | Frontend |
Frontend asla /ocpp/1.6/* kanalına bağlanmaz. Bu yanlış kullanım 4400 close ile reddedilir (charger CPID format değil).
Faz 4'te eklenecek WS event tipleri (/ws/telemetry kanalında)
ocpp.status— connector status değişikliğiocpp.meter— live MeterValues (downsample edilmiş)ocpp.session_started— yeni transactionocpp.session_stopped— transaction kapanışıocpp.command_result— REST komut yanıtı (frontend "loading" state'i kapatır)ocpp.boot— charger BootNotification kabulocpp.firmware_status— UpdateFirmware ilerleme
12. Provisioning Kit Güvenliği
Karar
POST /api/ocpp/chargers ve POST /api/ocpp/chargers/{id}/rotate-password response'unda plaintext password bir kez döner.
{
"id": "uuid",
"cpid": "ZEUS-AB12CD34EF56",
"password": "<32-char random>",
"hostname": "ems.example.com",
"port": 443,
"path": "/ocpp/1.6/",
"provisioning_kit_pdf_url": "/api/ocpp/chargers/{id}/provisioning-kit.pdf"
}
Zorunlu Güvenlik Kontrolleri
- OpenAPI schema:
passwordfieldformat: password,writeOnly: true. Swagger maskeler. - Audit log: Backend
record_security_event(event="ocpp.charger.password_revealed", actor=current_user.id, charger_id=charger.id)— kim/ne zaman/hangi charger. - Response header:
Cache-Control: no-store, no-cache, must-revalidate,Pragma: no-cache. - Permission:
MANAGE_CHARGER_SECRETSzorunlu. - Rate limit: Tenant başına
rotate-passwordendpoint'i 5dk'da en fazla 10 çağrı. - Provisioning kit PDF: Password PDF'te YAZILMAZ (sadece URL alanları + CPID + saha checklist'i + LED kodları). Password ayrı kanal (UI clipboard). Bu sızıntı yüzeyini düşürür.
13. OpenAPI Dokümantasyon
Karar
- Her endpoint'te
summary,description(markdown),responses(200, 4xx, 5xx ile schema),tags=["OCPP - <Resource>"]. examplesPydanticField(examples=[...])ile zenginleştirilmiş.- WebSocket endpoint'leri OpenAPI'da görünmez (FastAPI sınırlaması) ama
description'da ayrı Saha Entegrasyon Kılavuzu'na link. - Tag organizasyonu:
OCPP - ChargersOCPP - SessionsOCPP - Commands(PR-E5'tecommand-logsendpoint'i bu tag altında)OCPP - Charging ProfilesOCPP - Local Auth List(charger-spesifik override)OCPP - Tenant Auth List(PR-E4 — tenant-wide RFID kart family)OCPP - ReservationsOCPP - ProvisioningOCPP - Reports
Faz 3 Deliverable
api-documentation-architect agent her endpoint için OpenAPI spec doğrular; eksik schema/example varsa raporlar.
14. Versiyonlama ve Geriye Dönük Uyumluluk Politikası
Karar
- REST API path versioning yok (
/api/ocpp/); ileride breaking değişiklik için/api/v2/ocpp/ile coexist. - Schema değişiklikleri: Yeni field opsiyonel default'la eklenir (non-breaking); silmek için 1 release deprecation period (header
Deprecation: <date>). - Error code'lar: Yeni eklemeler non-breaking; mevcut kod silmek breaking.
- WebSocket close code'ları: Mevcut kodlar (
4400/4401/4404/4429/4503) sabit; yeni kodlar 4500+ aralığında eklenir.
Breaking Change Süreci
- Issue/RFC açılır → tartışılır.
- Changelog'a deprecation notice eklenir (≥30 gün önceden).
- Yeni versiyon yayımlanır + eski mevcut kalır.
- Sahaki tüm charger'lar yeni versiyona migrate edildikten sonra eski kaldırılır.
Faz 3 Çıktı Listesi (tamamlandı — PR #265)
| # | İş | Dosya | Sorumlu Agent |
|---|---|---|---|
| 1 | Pydantic schemas | backend/app/features/ocpp/schemas.py | backend-systems-architect |
| 2 | Service katmanı (business logic) | backend/app/features/ocpp/service.py | backend-systems-architect |
| 3 | REST router (~22 endpoint) | backend/app/features/ocpp/router.py | backend-systems-architect |
| 4 | Permission enum genişletme | backend/app/core/security/permissions.py | backend-systems-architect |
| 5 | Idempotency middleware/dependency | backend/app/features/ocpp/idempotency.py | backend-systems-architect |
| 6 | Komut bridge (REST → dispatcher) | backend/app/features/ocpp/commands.py | backend-systems-architect |
| 7 | Provisioning kit PDF üretici | backend/app/features/ocpp/provisioning_pdf.py | backend-systems-architect |
| 8 | Alarm engine entegrasyonu (Faz 2 TODO) | backend/app/features/alarms/engine.py | backend-systems-architect |
| 9 | Error envelope canonical handler | backend/app/main.py | backend-systems-architect |
| 10 | OpenAPI doc + examples | her endpoint | api-documentation-architect |
| 11 | Integration test suite | backend/tests/features/ocpp/test_rest_api.py | testing-qa-architect |
| 12 | Audit gate | tüm dosyalar | code-audit-sentinel |
Doküman versiyonu: 1.0 Sonraki güncelleme: Faz 6 implementasyonu sırasında karar değişiklikleri.