`DefineRec.InputObject` with `fixFields`?
まだ誰も着手していません。
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 45/100
- issue の種類
- 機能追加
- 明瞭さ
- おおむね明確
- 活発さ
- 活発
- 技術スタック
- fsharp
調査の方向性
Issue に示されている Define.InputObject と提案された DefineRec.InputObject のエントリーポイントから始め、現在、再帰的な input 定義がどのように表現されているかを調べてください。ProductFilter の完全なデモを実行し、公開 helper が警告なしで再帰的なフィールドをサポートすべきかを判断してください;合意された API と、検証済みの再帰的スキーマ例が得られれば完了です。
索引モデルが issue の本文から書いたものです。
説明
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"
}
]
}
- 主要言語
- F#
- スター
- 406
- フォーク
- 74
- 平均マージ
- 1日 8時間
- マージ済み PR(30日)
- 14
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
fsprojects/FSharp.Data.GraphQL のほかの issue
-
難易度 4/5 3〜5日 初心者へのやさしさ 52/100
-
fsprojects/FSharp.Data.GraphQL#573 · リアクション 1 件 · 担当者 2 名 ·
-
FR: Suave package オープン
fsprojects/FSharp.Data.GraphQL#566 · コメント 1 件 · リアクション 1 件 · 担当者 2 名 ·
-
難易度 5/5 1週間以上 初心者へのやさしさ 30/100
-
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
fsprojects/FSharp.Data.GraphQL の issue をすべて見る
似ている issue
-
Area: Excel support
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
orbeon/orbeon-forms#7893 ·
-
essnmx good first issue
難易度 1/5 1時間未満 初心者へのやさしさ 95/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
yeti-platform/yeti#1380 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
modelcontextprotocol/python-sdk#3566 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
griptape-ai/griptape#2353 ·