diff options
Diffstat (limited to 'src/FundLab.Api/App.fs')
| -rw-r--r-- | src/FundLab.Api/App.fs | 75 |
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 |
