summaryrefslogtreecommitdiff
path: root/src/FundLab.Api/App.fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/FundLab.Api/App.fs')
-rw-r--r--src/FundLab.Api/App.fs75
1 files changed, 66 insertions, 9 deletions
diff --git a/src/FundLab.Api/App.fs b/src/FundLab.Api/App.fs
index 370f503..e359af9 100644
--- a/src/FundLab.Api/App.fs
+++ b/src/FundLab.Api/App.fs
@@ -337,6 +337,27 @@ type MarketNavDatesApiResponse =
dates: string list
}
+type MarketNavSeriesPointApiResponse =
+ {
+ navDate: string
+ nav: string
+ accumulatedNav: string option
+ }
+
+type MarketNavSeriesApiResponse =
+ {
+ code: string
+ points: MarketNavSeriesPointApiResponse list
+ }
+
+/// The AKShare-backed probes the market endpoints depend on. Injected together
+/// so the app stays repository-only unless a caller really wants market routes.
+type MarketProbes =
+ {
+ NavDates: INavDateProbe
+ NavSeries: INavSeriesProbe
+ }
+
module App =
let addOptionFriendlyJson (services: IServiceCollection) =
let settings =
@@ -1649,13 +1670,49 @@ module App =
json ({ code = code.Trim(); dates = dates |> List.map dateText } : MarketNavDatesApiResponse) next ctx
| Error failure -> marketDataError failure next ctx
- let private navDateProbeRoutes (probe: INavDateProbe) =
- [ GET >=> route "/market/nav-dates" >=> getMarketNavDates probe ]
+ let private marketNavSeriesPointResponse (point: NavSeriesPoint) : MarketNavSeriesPointApiResponse =
+ {
+ navDate = dateText point.NavDate
+ nav = decimalText point.Nav
+ accumulatedNav = point.AccumulatedNav |> Option.map decimalText
+ }
+
+ let private getMarketNavSeries (probe: INavSeriesProbe) : HttpHandler =
+ fun next ctx ->
+ let code = ctx.Request.Query["code"].ToString()
+ let limitText = ctx.Request.Query["limit"].ToString()
+
+ let limit =
+ if String.IsNullOrWhiteSpace limitText then
+ Ok 30
+ else
+ match Int32.TryParse(limitText, NumberStyles.Integer, invariant) with
+ | true, value when value >= 1 && value <= 250 -> Ok value
+ | _ -> Error "limit must be an integer between 1 and 250"
+
+ match limit with
+ | Error message -> marketDataError (InvalidMarketDataRequest message) next ctx
+ | Ok value ->
+ match probe.RecentNavSeries(code, value, ctx.RequestAborted) with
+ | Ok points ->
+ json
+ ({ code = code.Trim()
+ points = points |> List.map marketNavSeriesPointResponse }
+ : MarketNavSeriesApiResponse)
+ next
+ ctx
+ | Error failure -> marketDataError failure next ctx
+
+ let private marketProbeRoutes (probes: MarketProbes) =
+ [
+ GET >=> route "/market/nav-dates" >=> getMarketNavDates probes.NavDates
+ GET >=> route "/market/nav-series" >=> getMarketNavSeries probes.NavSeries
+ ]
let private createApplicationInternal
(repository: FundRepository)
(marketData: IMarketDataService option)
- (navDateProbe: INavDateProbe option)
+ (probes: MarketProbes option)
: HttpHandler =
let apiRoutes =
[
@@ -1687,7 +1744,7 @@ module App =
GET >=> routef "/funds/%s" (getFund repository)
]
@ (marketData |> Option.map marketDataRoutes |> Option.defaultValue [])
- @ (navDateProbe |> Option.map navDateProbeRoutes |> Option.defaultValue [])
+ @ (probes |> Option.map marketProbeRoutes |> Option.defaultValue [])
choose [
GET >=> route "/health" >=> health
@@ -1701,15 +1758,15 @@ module App =
let createApplicationWithMarketData (repository: FundRepository) (marketData: IMarketDataService) : HttpHandler =
createApplicationInternal repository (Some marketData) None
- let createApplicationWithNavDateProbe (repository: FundRepository) (probe: INavDateProbe) : HttpHandler =
- createApplicationInternal repository None (Some probe)
+ let createApplicationWithProbes (repository: FundRepository) (probes: MarketProbes) : HttpHandler =
+ createApplicationInternal repository None (Some probes)
- let createApplicationWithMarketDataAndProbe
+ let createApplicationWithMarketDataAndProbes
(repository: FundRepository)
(marketData: IMarketDataService)
- (probe: INavDateProbe)
+ (probes: MarketProbes)
: HttpHandler =
- createApplicationInternal repository (Some marketData) (Some probe)
+ createApplicationInternal repository (Some marketData) (Some probes)
let createApplication (repository: FundRepository) : HttpHandler =
createApplicationInternal repository None None