Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

`DefineRec.InputObject` with `fixFields`?

Abierto
#596 0 comentarios 0 reacciones 0 asignados Ver en GitHub

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

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

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de fsprojects/FSharp.Data.GraphQL

Todos los issues de fsprojects/FSharp.Data.GraphQL

Issues similares

Más issues de Backend & API Design

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.