open Giraffe open Microsoft.AspNetCore.Http open Microsoft.AspNetCore.Builder open Microsoft.Extensions.DependencyInjection open Microsoft.Extensions.Hosting open Thoth.Json type QueryFilter = | Equal of string * string | GreaterThan of string * float | LessThan of string * float type QueryDSL = { Select : string list Filters : QueryFilter list Sort : (string * string) list // Field and direction } let parseQuery (body: string) = // Example JSON parsing (using FSharp.Data or similar) // Example query JSON: // { "select": ["name", "age"], "filters": [{"field": "age", "op": "gt", "value": 30}], "sort": [["name", "asc"]] } try let query = Decode.Auto.fromString body // let parsed = FSharp.Data.JsonValue.Parse(body) //let selectFields = parsed.["select"].AsArray() |> Array.map string |> List.ofArray //let filters = // parsed.["filters"].AsArray() // |> Array.map (fun filter -> // match filter.["op"].AsString() with // | "eq" -> Equal(filter.["field"].AsString(), filter.["value"].AsString()) // | "gt" -> GreaterThan(filter.["field"].AsString(), filter.["value"].AsFloat()) // | "lt" -> LessThan(filter.["field"].AsString(), filter.["value"].AsFloat()) // | _ -> failwith "Unsupported filter") // |> List.ofArray //let sort = // parsed.["sort"].AsArray() // |> Array.map (fun s -> s.[0].AsString(), s.[1].AsString()) // |> List.ofArray //{ Select = selectFields; Filters = filters; Sort = sort } query with | ex -> failwithf "Failed to parse query: %s" ex.Message let fetchData (queryDsl: QueryDSL) = // Mock database query (replace with actual DB logic) let mockData = [ Map.ofList [ "name", box "Alice"; "age", box 30 ] Map.ofList [ "name", box "Bob"; "age", box 25 ] Map.ofList [ "name", box "Charlie"; "age", box 35 ] ] // Filter and transform the data based on the DSL mockData |> List.filter (fun row -> queryDsl.Filters |> List.forall (function | Equal(field, num) -> Map.tryFind field row |> Option.map(fun value -> (string value) = num) |> Option.defaultValue false //row.GetType().GetProperty(field).GetValue(row) = value //| GreaterThan(field, num) -> row.[field] |> float > num | GreaterThan(field, num) -> Map.tryFind field row |> Option.bind (fun value -> match value with | :? float as v -> Some (v > num) | :? int as v -> Some (float v > num) | _ -> None) |> Option.defaultValue false | LessThan(field, num) -> Map.tryFind field row |> Option.bind (fun value -> match value with | :? float as v -> Some (v < num) | :? int as v -> Some (float v < num) | _ -> None) |> Option.defaultValue false)) |> List.map (fun row -> queryDsl.Select |> List.map (fun field -> field, row.[field]) |> dict) |> fun result -> result let queryHandler = fun (next: HttpFunc) (ctx: HttpContext) -> task { //let! body = ctx.ReadBodyAsStringAsync() let! body = ctx.ReadBodyBufferedFromRequestAsync() return! parseQuery body |> Result.map(fun queryDsl -> let data = fetchData queryDsl json data next ctx ) |> Result.defaultWith (fun error -> json {|success = false;message = error|} next ctx) } let webApp () = POST >=> route "query" >=> queryHandler // [ //subRoute "/foo" [ GET [ route "/bar" (text "Aloha!") ] ] // POST // route "query" queryHandler // RequestErrors.notFound "Not Found" // ] let configureApp (app: IApplicationBuilder) = app.UseGiraffe queryHandler let configureServices (services: IServiceCollection) = services.AddGiraffe() |> ignore [] let main args = Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(fun webBuilder -> webBuilder //.Configure(configureApp) .ConfigureServices(configureServices) |> ignore) .Build() .Run() 0