Add GET /api/currencies/:date endpoint for historical EUR/DKK rates
Returns rates for a given date: today uses latest DB row, past dates check DB first then fetch from Frankfurter and persist for caching. Future dates and invalid formats return 400. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
(:require
|
||||
[clojure.data.json :as json]
|
||||
[integrant.core :as ig]
|
||||
[pmagnus.btcdata.frankfurter.rates :as rates]
|
||||
[pmagnus.btcdata.web.controllers.health :as health]
|
||||
[pmagnus.btcdata.web.middleware.exception :as exception]
|
||||
[pmagnus.btcdata.web.middleware.formats :as formats]
|
||||
@@ -12,6 +13,8 @@
|
||||
[reitit.swagger :as swagger])
|
||||
(:import
|
||||
[io.undertow.websockets.core WebSockets WebSocketChannel]
|
||||
[java.time LocalDate]
|
||||
[java.time.format DateTimeParseException]
|
||||
[java.util.concurrent LinkedBlockingQueue TimeUnit]))
|
||||
|
||||
(defn- price->json [{:keys [price prev-price recorded-at]} rates]
|
||||
@@ -95,6 +98,51 @@
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body (json/write-str {:error "No price data yet"})})))
|
||||
|
||||
(defn- rate-row->json [row]
|
||||
(json/write-str {:rate_date (str (:rate_date row))
|
||||
:eur (str (:eur row))
|
||||
:dkk (str (:dkk row))
|
||||
:eur_dkk (str (:eur_dkk row))}))
|
||||
|
||||
(defn- currencies-handler [{:keys [query-fn frankfurter]} req]
|
||||
(let [date-str (get-in req [:path-params :date])]
|
||||
(try
|
||||
(let [date (LocalDate/parse date-str)
|
||||
today (LocalDate/now)]
|
||||
(cond
|
||||
(.isAfter date today)
|
||||
{:status 400
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body (json/write-str {:error "Future date not allowed"})}
|
||||
|
||||
(.isEqual date today)
|
||||
(if-let [row (query-fn :get-latest-currency-rates {})]
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body (rate-row->json row)}
|
||||
{:status 404
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body (json/write-str {:error "No rates available yet"})})
|
||||
|
||||
:else
|
||||
(if-let [row (query-fn :get-currency-rates-by-date {:rate-date date})]
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body (rate-row->json row)}
|
||||
(if-let [result (rates/fetch-and-persist-for-date!
|
||||
(:client frankfurter) (:url frankfurter)
|
||||
query-fn date)]
|
||||
{:status 200
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body (rate-row->json result)}
|
||||
{:status 502
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body (json/write-str {:error "Failed to fetch rates from Frankfurter"})}))))
|
||||
(catch DateTimeParseException _
|
||||
{:status 400
|
||||
:headers {"Content-Type" "application/json"}
|
||||
:body (json/write-str {:error "Invalid date format, use YYYY-MM-DD"})}))))
|
||||
|
||||
(defn- api-routes [opts]
|
||||
[["/swagger.json"
|
||||
{:get {:no-doc true
|
||||
@@ -108,6 +156,8 @@
|
||||
:middleware []}]
|
||||
["/price/latest"
|
||||
{:get (fn [req] (latest-price-handler opts req))}]
|
||||
["/currencies/:date"
|
||||
{:get (fn [req] (currencies-handler opts req))}]
|
||||
["/strike/ws"
|
||||
{:get (fn [req] (ws-strike-handler opts req))
|
||||
:no-doc true
|
||||
|
||||
Reference in New Issue
Block a user