`DefineRec.InputObject` with `fixFields`?
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 3/5
- Temps estimé
- 1-2 jours
- Accessibilité débutants
- 45/100
- Type d'issue
- Fonctionnalité
- Clarté
- Plutôt claire
- Activité
- Active
- Stack technique
- fsharp
- Domaine
- api, backend-api-design
Piste de recherche
Commencez par les points d’entrée Define.InputObject et DefineRec.InputObject proposés et présentés dans l’issue, puis examinez comment les définitions d’input récursives sont actuellement représentées. Exécutez la démo complète de ProductFilter et déterminez si le helper public devrait prendre en charge les champs récursifs sans avertissements ; le travail est considéré comme terminé lorsqu’une API convenue et un exemple validé de schéma récursif sont disponibles.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
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"
}
]
}
- Langage dominant
- F#
- Étoiles
- 406
- Forks
- 74
- Merge moyen
- 15 h 59 min
- PR mergées (30 j)
- 20
Préparer son environnement
Ce projet ne fournit ni conteneur de développement, ni Dockerfile, ni guide de contribution : l'installation est à votre charge. Commencez par son README, et consultez notre guide de la première contribution pour les étapes générales.
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de fsprojects/FSharp.Data.GraphQL
-
Introspection emits JSON-serialized value as `defaultValue` instead of a GraphQL literal (breaks enum defaults)Peut-être pris @xperiandri l’a pris il y a 7 jours. Ouverte
Difficulté 4/5 3-5 jours Accessibilité débutants 65/100
fsprojects/FSharp.Data.GraphQL#623 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 4/5 3-5 jours Accessibilité débutants 52/100
fsprojects/FSharp.Data.GraphQL#613 ·
Les mainteneurs répondent en général sous 1 jour
-
`documentId` has different values for the same query contentPeut-être pris @xperiandri l’a pris il y a 146 jours. Ouverte
fsprojects/FSharp.Data.GraphQL#573 · 1 réaction · 2 personnes assignées ·
Les mainteneurs répondent en général sous 1 jour
-
FR: Suave packagePeut-être pris @xperiandri l’a pris il y a 146 jours. Ouverte
fsprojects/FSharp.Data.GraphQL#566 · 1 commentaire · 1 réaction · 2 personnes assignées ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 5/5 Plus d'une semaine Accessibilité débutants 30/100
fsprojects/FSharp.Data.GraphQL#542 ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de fsprojects/FSharp.Data.GraphQL
Issues similaires
-
Difficulté 1/5 Moins d'une heure Accessibilité débutants 72/100
supadata-ai/mcp#27 ·
-
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
objectionary/sodg.rs#300 ·
-
lane: fast
Difficulté 2/5 1-3 heures Accessibilité débutants 62/100
unicef/adt-studio#946 ·
Les mainteneurs répondent en général sous 2 jours
-
good first issue help wanted track:code
Difficulté 1/5 Moins d'une heure Accessibilité débutants 88/100
Sara-Managed-Projects/space-radar#755 ·
Les mainteneurs répondent en général sous 1 jour
-
buffer translateToString()/getChars() return '' for empty cells, collapsing cursor-positioned textOuverte
Difficulté 2/5 1-3 heures Accessibilité débutants 75/100
coder/ghostty-web#205 ·