diff --git a/modules/fx-rates/src/Api/Router.php b/modules/fx-rates/src/Api/Router.php index 350e907..7a09472 100644 --- a/modules/fx-rates/src/Api/Router.php +++ b/modules/fx-rates/src/Api/Router.php @@ -22,6 +22,10 @@ final class Router $this->respond(['ok' => true, 'module' => 'fx-rates']); } + if ($path === 'v1/endpoints' && $method === 'GET') { + $this->respond(['data' => $this->endpointCatalog()]); + } + if ($path === 'v1/status' && $method === 'GET') { $this->respond(['data' => $this->service->latestStatuses()]); } @@ -171,4 +175,96 @@ final class Router { return is_numeric($value) ? (int) $value : null; } + + private function endpointCatalog(): array + { + return [ + 'module' => 'fx-rates', + 'version' => 'v1', + 'languages' => ['de', 'en'], + 'endpoints' => [ + [ + 'path' => '/api/fx-rates/v1/endpoints', + 'method' => 'GET', + 'description_de' => 'Gibt alle verfuegbaren FX-API-Endpunkte mit deutscher und englischer Erklaerung zurueck.', + 'description_en' => 'Returns all available FX API endpoints with German and English explanations.', + ], + [ + 'path' => '/api/fx-rates/v1/latest', + 'method' => 'GET', + 'params' => ['base', 'symbols'], + 'description_de' => 'Liefert den neuesten gespeicherten Snapshot, optional auf eine Zielbasis umgerechnet und auf ausgewaehlte Waehrungen gefiltert.', + 'description_en' => 'Returns the latest stored snapshot, optionally rebased to a target currency and filtered to selected symbols.', + ], + [ + 'path' => '/api/fx-rates/v1/fetch', + 'method' => 'GET', + 'params' => ['fetch_id', 'base', 'symbols'], + 'description_de' => 'Liefert einen gespeicherten Snapshot anhand der fetch_id, optional umgerechnet auf eine Zielbasis und gefiltert auf einzelne Waehrungen.', + 'description_en' => 'Returns a stored snapshot by fetch_id, optionally rebased to a target currency and filtered to selected symbols.', + ], + [ + 'path' => '/api/fx-rates/v1/nearest', + 'method' => 'GET', + 'params' => ['at', 'base', 'symbols', 'window_minutes'], + 'description_de' => 'Liefert den zeitlich naechsten gespeicherten Snapshot zu einem Datum/Uhrzeit-Wert.', + 'description_en' => 'Returns the stored snapshot nearest to a given date/time value.', + ], + [ + 'path' => '/api/fx-rates/v1/snapshot', + 'method' => 'GET', + 'params' => ['at', 'base', 'symbols', 'window_minutes'], + 'description_de' => 'Liefert einen Snapshot zur Zielbasis und sucht fuer einen Zeitpunkt den naechsten passenden gespeicherten Kurs.', + 'description_en' => 'Returns a snapshot for the requested base and finds the nearest matching stored rate for a given timestamp.', + ], + [ + 'path' => '/api/fx-rates/v1/rate', + 'method' => 'GET', + 'params' => ['from', 'to', 'at', 'window_minutes'], + 'description_de' => 'Liefert einen Einzelkurs zwischen zwei Waehrungen, direkt oder als Kreuzkurs aus gespeicherten Snapshots.', + 'description_en' => 'Returns a single rate between two currencies, directly or as a cross-rate from stored snapshots.', + ], + [ + 'path' => '/api/fx-rates/v1/history', + 'method' => 'GET', + 'params' => ['from', 'to', 'from_at', 'to_at', 'limit'], + 'description_de' => 'Liefert den gespeicherten Kursverlauf zwischen zwei Waehrungen fuer einen Zeitraum.', + 'description_en' => 'Returns the stored rate history between two currencies for a given time range.', + ], + [ + 'path' => '/api/fx-rates/v1/refresh', + 'method' => 'POST', + 'body' => ['base', 'force', 'max_age_minutes'], + 'description_de' => 'Aktualisiert Kurse nur dann neu, wenn der letzte Abruf aelter als die erlaubte Zeitspanne ist. Die Antwort enthaelt immer die fetch_id des verwendeten Snapshots.', + 'description_en' => 'Refreshes rates only if the last fetch is older than the allowed age. The response always includes the fetch_id of the snapshot used.', + ], + [ + 'path' => '/api/fx-rates/v1/status', + 'method' => 'GET', + 'description_de' => 'Liefert den neuesten gespeicherten Abruf je Basiswaehrung.', + 'description_en' => 'Returns the most recent stored fetch per base currency.', + ], + [ + 'path' => '/api/fx-rates/v1/recent-fetches', + 'method' => 'GET', + 'params' => ['limit'], + 'description_de' => 'Liefert die zuletzt gespeicherten Abrufe mit fetch_id und Zeitstempel.', + 'description_en' => 'Returns the most recently stored fetches including fetch_id and timestamp.', + ], + [ + 'path' => '/api/fx-rates/v1/probe', + 'method' => 'GET', + 'params' => ['base'], + 'description_de' => 'Prueft, ob der konfigurierte Provider aktuelle Kurse liefern kann.', + 'description_en' => 'Checks whether the configured provider can return current rates.', + ], + [ + 'path' => '/api/fx-rates/v1/settings', + 'method' => 'GET', + 'description_de' => 'Liefert die aktuellen Modul-Settings.', + 'description_en' => 'Returns the current module settings.', + ], + ], + ]; + } } diff --git a/modules/fx-rates/src/Domain/FxRatesService.php b/modules/fx-rates/src/Domain/FxRatesService.php index 00e2e2e..22cbd26 100644 --- a/modules/fx-rates/src/Domain/FxRatesService.php +++ b/modules/fx-rates/src/Domain/FxRatesService.php @@ -218,6 +218,7 @@ final class FxRatesService 'rate_date' => $rateDate, 'updated_count' => count($saved['rates'] ?? []), 'rates' => $saved['rates'] ?? [], + 'fetch_id' => isset($saved['fetch']['id']) ? (int) $saved['fetch']['id'] : null, 'fetch' => $this->localizeFetch(is_array($saved['fetch'] ?? null) ? $saved['fetch'] : null), ]; } @@ -235,6 +236,7 @@ final class FxRatesService 'rate_date' => $latest['rate_date'] ?? null, 'updated_count' => 0, 'rates' => [], + 'fetch_id' => isset($latest['id']) ? (int) $latest['id'] : null, 'fetch' => $this->localizeFetch($latest), 'reused' => true, ];