将长生不老药苦艾酒 input_object 映射到单个数据结构
Mapping elixir absinthe input_object to single data structure
有什么方法可以将 input_object 映射到单个数据结构中吗?
input_object(:numrange) do
field :start, non_null(:integer) do
resolve(fn field, _, _ ->
...
end)
end
field :end, non_null(:integer) do
resolve(fn field, _, _ ->
...
end)
end
end
然后将其解析为 [start, end] ?
不幸的是,无法在 input_object 级别定义它。但是,有几种不同的方法来处理参数转换。
1。更改您的业务逻辑
您可以只更改服务层中的契约来处理对象而不是数组。
# Inside of some module
def my_functon(attrs, other, params) do
formatted_attrs = case attrs do
%{range: %{start: startrange, end: endrange}} ->
%{attrs | range: [startrange, endrange]}
_ ->
attrs
end
# ...
end
2。处理架构中 args 传递的输入对象
您可以在提交参数时转换参数
# Some graphql definition
field(:my_field, :my_object) do
arg(:range, non_null(:numrange))
resolve(fn parent, %{range: %{start: rstart, end: rend}} = args, ctx ->
new_args = %{args | range: [rstart, rend]}
SomeModule.my_function(new_attrs, parent, ctx)
end)
end
3。创建一个中间件
您可以创建一个 Absinthe.Middleware 来在提交参数时转换参数
defmodule MyApp.NumrangeTransform do
@behaviour Absinthe.Middleware
@impl Absinthe.Middleware
def call(%Absinthe.Resolution{arguments: args} = res, opts) do
field = Keyword.fetch!(opts, :field)
new_args = case Map.get(args, field) do
%{start: rstart, end: rend} ->
Map.put(args, field, [rstart, rend])
_ ->
args
end
%{res | arguments: new_args}
end
end
然后在您的架构定义中:
field(:my_field, :type) do
middleware(MyApp.NumrangeTransform, field: :range)
arg(:range, :numrange)
# ...
end
中间件会为您转换参数,而无需到处编写转换逻辑
4。创建自定义标量类型
custom scalar 类型可以在 Absinthe 中定义:
# In some schema definition
scalar :numrange, name: "NumRange" do
description("A number range of integers m..n")
serialize([rstart, rend]) when is_integer(rstart) and is_integer(rend) do
rstart <> ".." <> rend
end
parse(&do_parse/1)
defp do_parse(%Absinthe.Blueprint.Input.String{value: range_str}) do
with [s_str, e_str] <- String.split(range_str, ".."),
{rstart, _} <- Integer.parse(s_str),
{rend, _} <- Integer.parse(e_str) do
{:ok, [rstart, rend]}
else
_ -> :error
end
end
def do_parse(%Absinthe.Blueprint.Input.Null{}), do: {:ok, nil}
def do_parse(_), do: :error
end
然后将其添加到架构中的某处
field(:my_field, :type) do
arg(:range, non_null(:numrange))
# ...
end
GraphQL 看起来像这样:
query SomeQuery {
myField(range:"1..3")
}
这可能是最没有吸引力的选项,因为它创建了一种非标准的方式来为任何前端应用程序呈现和接受数字范围。但是,如果这不是第三方应用程序访问的public API,那么这样做应该没有问题。
结论
有很多方法可以定义和处理输入参数中的参数转换。可能还有其他我没有提到的解决方案。您可能会通过编写自定义 Absinthe.Phase 来做一些非常疯狂的事情,但这是一项复杂的工作,对于这么简单的事情来说可能过于笨重。
有什么方法可以将 input_object 映射到单个数据结构中吗?
input_object(:numrange) do
field :start, non_null(:integer) do
resolve(fn field, _, _ ->
...
end)
end
field :end, non_null(:integer) do
resolve(fn field, _, _ ->
...
end)
end
end
然后将其解析为 [start, end] ?
不幸的是,无法在 input_object 级别定义它。但是,有几种不同的方法来处理参数转换。
1。更改您的业务逻辑
您可以只更改服务层中的契约来处理对象而不是数组。
# Inside of some module
def my_functon(attrs, other, params) do
formatted_attrs = case attrs do
%{range: %{start: startrange, end: endrange}} ->
%{attrs | range: [startrange, endrange]}
_ ->
attrs
end
# ...
end
2。处理架构中 args 传递的输入对象
您可以在提交参数时转换参数
# Some graphql definition
field(:my_field, :my_object) do
arg(:range, non_null(:numrange))
resolve(fn parent, %{range: %{start: rstart, end: rend}} = args, ctx ->
new_args = %{args | range: [rstart, rend]}
SomeModule.my_function(new_attrs, parent, ctx)
end)
end
3。创建一个中间件
您可以创建一个 Absinthe.Middleware 来在提交参数时转换参数
defmodule MyApp.NumrangeTransform do
@behaviour Absinthe.Middleware
@impl Absinthe.Middleware
def call(%Absinthe.Resolution{arguments: args} = res, opts) do
field = Keyword.fetch!(opts, :field)
new_args = case Map.get(args, field) do
%{start: rstart, end: rend} ->
Map.put(args, field, [rstart, rend])
_ ->
args
end
%{res | arguments: new_args}
end
end
然后在您的架构定义中:
field(:my_field, :type) do
middleware(MyApp.NumrangeTransform, field: :range)
arg(:range, :numrange)
# ...
end
中间件会为您转换参数,而无需到处编写转换逻辑
4。创建自定义标量类型
custom scalar 类型可以在 Absinthe 中定义:
# In some schema definition
scalar :numrange, name: "NumRange" do
description("A number range of integers m..n")
serialize([rstart, rend]) when is_integer(rstart) and is_integer(rend) do
rstart <> ".." <> rend
end
parse(&do_parse/1)
defp do_parse(%Absinthe.Blueprint.Input.String{value: range_str}) do
with [s_str, e_str] <- String.split(range_str, ".."),
{rstart, _} <- Integer.parse(s_str),
{rend, _} <- Integer.parse(e_str) do
{:ok, [rstart, rend]}
else
_ -> :error
end
end
def do_parse(%Absinthe.Blueprint.Input.Null{}), do: {:ok, nil}
def do_parse(_), do: :error
end
然后将其添加到架构中的某处
field(:my_field, :type) do
arg(:range, non_null(:numrange))
# ...
end
GraphQL 看起来像这样:
query SomeQuery {
myField(range:"1..3")
}
这可能是最没有吸引力的选项,因为它创建了一种非标准的方式来为任何前端应用程序呈现和接受数字范围。但是,如果这不是第三方应用程序访问的public API,那么这样做应该没有问题。
结论
有很多方法可以定义和处理输入参数中的参数转换。可能还有其他我没有提到的解决方案。您可能会通过编写自定义 Absinthe.Phase 来做一些非常疯狂的事情,但这是一项复杂的工作,对于这么简单的事情来说可能过于笨重。