elixir - 将 elixir 苦艾酒 input_object 映射到单个数据结构

标签 elixir absinthe

是否有某种方法可以将 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类型可以在苦艾酒中定义:

# 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 来做一些非常疯狂的事情,但这是一项复杂的工作,对于如此简单的事情来说可能过于严厉。

关于elixir - 将 elixir 苦艾酒 input_object 映射到单个数据结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68728770/

相关文章:

elixir - 尝试在 Phoenix 中发送 HTTP 状态代码时出错

elixir - 如何为 Elixir 结构验证/强制执行类型和值?

Phoenix同表多对多

elixir - Absinthe 后端的正确 GraphQL 查询中的未知参数

elixir - Graphql Absinthe Elixir 基于权限的可访问字段

elixir - 如何找到子字符串的索引?

python - Erlport/Python STDOUT 捕获到 Elixir

elixir - 如何从 Phoenix 应用程序中删除 Ecto 模型