Ana içeriğe geç

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)

EndpointPRAçıklama
GET /chargers?subregion_id={uuid}PR-E2 (#287)subregion_id query parametre + response subregion_id / subregion_name field'ları.
GET /chargers/{id}/reservationsPR-E5 (#290)Rezervasyon listesi (CancelReservation dropdown). status filter `active
GET /chargers/{id}/command-logsPR-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/tenantPR-E4 (#289)Tenant-wide RFID kart listesi (charger_id IS NULL). Permission: READ_CHARGER.
POST /auth-list/tenantPR-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/pushPR-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 (Pydantic Literal whitelist; geçersiz değer 422). Default active.
  • 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 | null
  • subregion_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 StatusYanıt Body
Accepted200 OK{"status": "accepted", ...} + komut-spesifik field'lar
Rejected409 Conflicterror_code: OCPP_REMOTE_REJECTED
NotImplemented (charger desteklemiyor)501 Not Implementederror_code: OCPP_NOT_IMPLEMENTED
NotSupported501 Not Implementederror_code: OCPP_NOT_SUPPORTED
Charger offline503 Service Unavailableerror_code: OCPP_CHARGER_OFFLINE
Dispatcher timeout (30 sn)504 Gateway Timeouterror_code: OCPP_COMMAND_TIMEOUT
Validation hatası400 Bad Requesterror_code: VALIDATION_ERROR, details: {...}
Yetki yok403 Forbiddenerror_code: PERMISSION_DENIED
Charger bulunamadı404 Not Founderror_code: NOT_FOUND
Idempotency replay200 OKCached yanıt + X-Idempotency-Replay: true header

Gerekçe

  • Rejected charger'ın iş kurallarınca reddi (örn. konnektör meşgul) → 409 (kaynak çatışması) semantik olarak doğru.
  • NotImplemented HTTP 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 veya OCPP_* 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: RemoteStartRequest mutex id_tag / parent_id_tag XOR ihlali dahil)
  • PERMISSION_DENIED — RBAC reddi
  • NOT_FOUND — Kaynak yok
  • OCPP_REMOTE_REJECTED — Charger Rejected
  • OCPP_NOT_IMPLEMENTED — Charger NotImplemented
  • OCPP_NOT_SUPPORTED — Charger NotSupported
  • OCPP_CHARGER_OFFLINE — Charger WS bağlı değil
  • OCPP_COMMAND_TIMEOUT — Dispatcher 30sn timeout
  • OCPP_INVALID_DOWNSAMPLE — Downsample formatı geçersiz
  • OCPP_INVALID_PROFILE_SCHEDULE — SmartCharging schedule schema reddi
  • OCPP_DUPLICATE_REQUEST — Idempotency window içinde tekrar (200 dönmeden, edge case)
  • OCPP_AUTH_LIST_DUPLICATE (PR-E4) — POST /auth-list/tenant tenant'ta aynı id_tag mevcut (409)
  • OCPP_PARENT_TAG_NO_ACTIVE_CARD (Issue #502, 404) — RemoteStartTransaction çağrısında verilen parent_id_tag için tenant altında aktif kart yok (status='Accepted' ve expiry_date gelecekte). Operator UI'da "Tanımlı aktif kart yok" mesajı görür ve RFID Kartları ekranına yönlendirilir.
  • OCPP_PARENT_TAG_DATA_INTEGRITY (Issue #502, 500) — 1:1 invariant ihlali: aynı parent_id_tag altında 2+ aktif kart. Backend LIMIT 2 ile yakalar; Migration 0056 partial UNIQUE INDEX (uq_auth_list_parent_per_tenant) prod'a indikten sonra DB-level fail-safe devreye girer. Operasyon ekibi docs/runbook/0056_parent_id_tag_cleanup.md runbook'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):

PermissionKapsam
READ_CHARGERListe + detay + session geçmişi + meter samples + rapor
WRITE_CHARGERCharger CRUD (create/update/delete) — iş tarafı meta verileri
CONTROL_CHARGERKomut RPC'leri (RemoteStart/Stop, Reset, Unlock, ChangeAvailability)
CONFIGURE_CHARGERChangeConfiguration, SetChargingProfile, SendLocalList, ReserveNow
OTA_CHARGERUpdateFirmware, GetDiagnostics
MANAGE_CHARGER_SECRETSPassword rotate + provisioning-kit.pdf indirme

Rol Şablonları (öneri)

RolPermission'lar
adminHepsi
operatorREAD_CHARGER, CONTROL_CHARGER, CONFIGURE_CHARGER (saha teknisyeni)
viewerREAD_CHARGER (raporlama, dashboard)
installerREAD_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/*):

  1. Opsiyonel header: Idempotency-Key: <client-uuid>
  2. Server-side dedup window: Header gönderilmemişse, server (charger_id, action, connector_id, id_tag) tuple üzerinde 10 saniyelik dedup.
  3. Cache TTL: Header gönderilmişse, server bu key için 24 saat boyunca yanıtı cache'ler.
  4. Replay yanıtı: Aynı key ile gelen ikinci istek → HTTP 200 + ilk yanıtın aynısı + X-Idempotency-Replay: true header.

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'ı):

ParametreDefaultMax
limit50200
offset0(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:

  • from ve to ISO 8601 UTC (örn. 2026-01-01T00:00:00Z); naive datetime reddedilir (400).
  • Default to = now(), default from = to - 24h.
  • from > to → 400 VALIDATION_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 parametresimeter-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_DOWNSAMPLE döner ve mesaj "explicit downsample gerekli" olur)
  • Validation error: OCPP_INVALID_DOWNSAMPLE 400

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).
  • null literal anlamı: Phase belirtilmedi (vendor tek-faz cihaz ve phase alanı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 RESTRICT ile 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ışı

  1. Pydantic schema validation (Zod-equivalent — chargingProfileKind × recurrencyKind kombinasyonları, chargingSchedulePeriods[] non-empty).
  2. DB INSERT (status='draft').
  3. Dispatcher SetChargingProfile push.
  4. Charger Accepted → status='pushed' → 200.
  5. 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_id filter 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:

  1. JWT'den tenant_id çıkarılır.
  2. Charger DB lookup WHERE id = :charger_id AND tenant_id = :tenant_id (tenant izolasyonu).
  3. Yoksa 404 NOT_FOUND (cross-tenant probe enumeration koruması — 403 yerine 404).
  4. Varsa dispatcher'a delegate.

11. WebSocket vs REST Ayrımı

Karar

Zeus 3 farklı WebSocket kanalı kullanır; net ayrım:

KanalYönAmaçKullanıcı
/ocpp/1.6/{cpid}charger ↔ backendOCPP protokolüSadece charger cihazları
/ws/telemetrybackend → frontendLive event push (status, meter, command results)Frontend
REST /api/ocpp/*frontend ↔ backendCRUD + komut RPCFrontend

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ği
  • ocpp.meter — live MeterValues (downsample edilmiş)
  • ocpp.session_started — yeni transaction
  • ocpp.session_stopped — transaction kapanışı
  • ocpp.command_result — REST komut yanıtı (frontend "loading" state'i kapatır)
  • ocpp.boot — charger BootNotification kabul
  • ocpp.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

  1. OpenAPI schema: password field format: password, writeOnly: true. Swagger maskeler.
  2. Audit log: Backend record_security_event(event="ocpp.charger.password_revealed", actor=current_user.id, charger_id=charger.id) — kim/ne zaman/hangi charger.
  3. Response header: Cache-Control: no-store, no-cache, must-revalidate, Pragma: no-cache.
  4. Permission: MANAGE_CHARGER_SECRETS zorunlu.
  5. Rate limit: Tenant başına rotate-password endpoint'i 5dk'da en fazla 10 çağrı.
  6. 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>"].
  • examples Pydantic Field(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 - Chargers
    • OCPP - Sessions
    • OCPP - Commands (PR-E5'te command-logs endpoint'i bu tag altında)
    • OCPP - Charging Profiles
    • OCPP - Local Auth List (charger-spesifik override)
    • OCPP - Tenant Auth List (PR-E4 — tenant-wide RFID kart family)
    • OCPP - Reservations
    • OCPP - Provisioning
    • OCPP - 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

  1. Issue/RFC açılır → tartışılır.
  2. Changelog'a deprecation notice eklenir (≥30 gün önceden).
  3. Yeni versiyon yayımlanır + eski mevcut kalır.
  4. Sahaki tüm charger'lar yeni versiyona migrate edildikten sonra eski kaldırılır.

Faz 3 Çıktı Listesi (tamamlandı — PR #265)

#İşDosyaSorumlu Agent
1Pydantic schemasbackend/app/features/ocpp/schemas.pybackend-systems-architect
2Service katmanı (business logic)backend/app/features/ocpp/service.pybackend-systems-architect
3REST router (~22 endpoint)backend/app/features/ocpp/router.pybackend-systems-architect
4Permission enum genişletmebackend/app/core/security/permissions.pybackend-systems-architect
5Idempotency middleware/dependencybackend/app/features/ocpp/idempotency.pybackend-systems-architect
6Komut bridge (REST → dispatcher)backend/app/features/ocpp/commands.pybackend-systems-architect
7Provisioning kit PDF üreticibackend/app/features/ocpp/provisioning_pdf.pybackend-systems-architect
8Alarm engine entegrasyonu (Faz 2 TODO)backend/app/features/alarms/engine.pybackend-systems-architect
9Error envelope canonical handlerbackend/app/main.pybackend-systems-architect
10OpenAPI doc + examplesher endpointapi-documentation-architect
11Integration test suitebackend/tests/features/ocpp/test_rest_api.pytesting-qa-architect
12Audit gatetüm dosyalarcode-audit-sentinel

Doküman versiyonu: 1.0 Sonraki güncelleme: Faz 6 implementasyonu sırasında karar değişiklikleri.