`DefineRec.InputObject` with `fixFields`?
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 45/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- fsharp
- Área
- api, backend-api-design
Línea de trabajo
Comience con los puntos de entrada Define.InputObject y el propuesto DefineRec.InputObject que se muestran en el issue, y después inspeccione cómo se representan actualmente las definiciones de input recursivas. Ejecute la demo completa de ProductFilter y determine si el helper público debería admitir campos recursivos sin advertencias; se considera terminado cuando haya una API acordada y un ejemplo validado de un esquema recursivo.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
For recursive input types it's nice not to have warnings.
I came up with this helper function:
type DefineRec with
static member InputObject(
name: string,
fixFields: InputObjectDefinition<'a> -> InputFieldDef list,
?description: string
) : InputObjectDefinition<'a> =
let mutable self = Unchecked.defaultof<InputObjectDefinition<'a>>
let definition =
{
Name = name
Fields = lazy (fixFields self |> List.toArray)
Description = description
Validator = Validation.GQLValidator.empty
ExecuteInput = Unchecked.defaultof<_>
}
self <- definition
definition
Minimal usage:
type Comment =
{
Message : string
Reply : Comment
}
DefineRec.InputObject<Comment>(
name = "Comment",
fixFields =
fun self ->
[
Define.Input("message", StringType)
Define.Input("reply", self)
]
)
Is this a good approach?
Full demo usage
#r "nuget: FSharp.Data.GraphQL.Server, 3.1.1"
open System.Text.Json
open FSharp.Data.GraphQL
open FSharp.Data.GraphQL.Types
type DefineRec with
static member InputObject(
name: string,
fixFields: InputObjectDefinition<'a> -> InputFieldDef list,
?description: string
) : InputObjectDefinition<'a> =
let mutable self = Unchecked.defaultof<InputObjectDefinition<'a>>
let definition =
{
Name = name
Fields = lazy (fixFields self |> List.toArray)
Description = description
Validator = Validation.GQLValidator.empty
ExecuteInput = Unchecked.defaultof<_>
}
self <- definition
definition
type StringFilter =
{
Eq : string option
Ne : string option
In : string list option
Nin : string list option
}
type ProductFilter =
{
Title : StringFilter option
Category : StringFilter option
Brand : StringFilter option
And : ProductFilter list option
Or : ProductFilter list option
Not : ProductFilter option
}
type Product =
{
ID : int
Title : string
Category : string
Brand : string
}
let stringFilterInputType : InputObjectDefinition<StringFilter> =
Define.InputObject(
name = "StringFilterInput",
fields =
[
Define.Input("eq", Nullable StringType)
Define.Input("ne", Nullable StringType)
Define.Input("in", Nullable (ListOf StringType))
Define.Input("nin", Nullable (ListOf StringType))
]
)
let productFilterInputType : InputObjectDefinition<ProductFilter> =
DefineRec.InputObject(
name = "ProductFilterInput",
fixFields =
fun self ->
[
Define.Input("title", Nullable stringFilterInputType)
Define.Input("category", Nullable stringFilterInputType)
Define.Input("brand", Nullable stringFilterInputType)
Define.Input("and", Nullable (ListOf self))
Define.Input("or", Nullable (ListOf self))
Define.Input("not", Nullable self)
]
)
let productType =
Define.Object<Product>(
name = "Product",
fields =
[
Define.Field("id", IntType, fun _ p -> p.ID)
Define.Field("title", StringType, fun _ p -> p.Title)
Define.Field("category", StringType, fun _ p -> p.Category)
Define.Field("brand", StringType, fun _ p -> p.Brand)
]
)
let evalStringFilter (targetVal : string) (filter : StringFilter) : bool =
let matchEq = filter.Eq |> Option.forall (fun v -> targetVal = v)
let matchNe = filter.Ne |> Option.forall (fun v -> targetVal <> v)
let matchIn = filter.In |> Option.forall (fun list -> List.contains targetVal list)
let matchNin = filter.Nin |> Option.forall (fun list -> not (List.contains targetVal list))
matchEq && matchNe && matchIn && matchNin
let rec matchesProduct (product : Product) (filter : ProductFilter) : bool =
let titleMatch = filter.Title |> Option.forall (evalStringFilter product.Title)
let categoryMatch = filter.Category |> Option.forall (evalStringFilter product.Category)
let brandMatch = filter.Brand |> Option.forall (evalStringFilter product.Brand)
let fieldsValid = titleMatch && categoryMatch && brandMatch
let andValid = filter.And |> Option.forall (List.forall (matchesProduct product))
let orValid = filter.Or |> Option.forall (List.exists (matchesProduct product))
let notValid = filter.Not |> Option.forall (fun f -> not (matchesProduct product f))
fieldsValid && andValid && orValid && notValid
let products =
[
{ ID = 1; Title = "Laptop"; Category = "Electronics"; Brand = "BrandA" }
{ ID = 2; Title = "Smartphone"; Category = "Electronics"; Brand = "BrandB" }
{ ID = 3; Title = "Headphones"; Category = "Accessories"; Brand = "BrandA" }
{ ID = 4; Title = "Coffee Maker"; Category = "Home Appliances"; Brand = "BrandC" }
{ ID = 5; Title = "Blender"; Category = "Home Appliances"; Brand = "BrandD" }
]
let fetchProducts (filter : ProductFilter option) =
match filter with
| Some f -> List.filter (fun p -> matchesProduct p f) products
| None -> products
let queryType =
Define.Object(
name = "Query",
fields = [
Define.Field(
name = "products",
typedef = ListOf productType,
args =
[
Define.Input("filter", Nullable productFilterInputType)
],
resolve =
fun ctx () ->
let filterArg : ProductFilter voption = ctx.TryArg("filter")
fetchProducts (Option.ofValueOption filterArg)
)
]
)
let schema = Schema(queryType)
let executor = Executor(schema)
let query =
"""
query {
products(
filter: {
or: [
{ category: { eq: "Electronics" } },
{ brand: { eq: "BrandA" } }
]
}
) {
id
title
category
brand
}
}
"""
let response =
executor.AsyncExecute(
query,
(fun () ->
{
new IInputExecutionContext with
member this.GetFile(_ : string) =
Result.Error "Not implemented"
})
)
|> Async.RunSynchronously
let content =
match response.Content with
| GQLResponseContent.Direct (content, []) -> content
| GQLResponseContent.Direct (_, errors) -> failwith $"Unexpected errors: {errors}"
| x -> failwith $"Unexpected response content: %A{x}"
let json = System.Text.Json.JsonSerializer.Serialize(content, JsonSerializerOptions(WriteIndented = true))
printfn "%s" json
{
"products": [
{
"id": 1,
"title": "Laptop",
"category": "Electronics",
"brand": "BrandA"
},
{
"id": 2,
"title": "Smartphone",
"category": "Electronics",
"brand": "BrandB"
},
{
"id": 3,
"title": "Headphones",
"category": "Accessories",
"brand": "BrandA"
}
]
}
- Lenguaje dominante
- F#
- Estrellas
- 406
- Forks
- 74
- Merge medio
- 1 d 8 h
- PR fusionados (30 d)
- 14
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de fsprojects/FSharp.Data.GraphQL
-
Dificultad 4/5 3-5 días Aptitud para principiantes 52/100
-
fsprojects/FSharp.Data.GraphQL#573 · 1 reacción · 2 asignados ·
-
FR: Suave package Abierto
fsprojects/FSharp.Data.GraphQL#566 · 1 comentario · 1 reacción · 2 asignados ·
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 30/100
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 25/100
Todos los issues de fsprojects/FSharp.Data.GraphQL
Issues similares
-
Area: Excel support
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
orbeon/orbeon-forms#7893 ·
-
essnmx good first issue
Dificultad 1/5 Menos de una hora Aptitud para principiantes 95/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
yeti-platform/yeti#1380 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
modelcontextprotocol/python-sdk#3566 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
griptape-ai/griptape#2353 ·