【问题标题】:Mapping elixir absinthe input_object to single data structure将长生不老药苦艾酒映射到单个数据结构
【发布时间】:2021-10-14 03:12:15
【问题描述】:

有没有办法将 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] ?

【问题讨论】:

    标签: elixir absinthe


    【解决方案1】:

    不幸的是,无法在 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")
    }
    

    这可能是最不吸引人的选项,因为它为任何前端应用程序创建了一种呈现和接受数字范围的非标准方式。但是,如果这不是第三方应用程序可以访问的公共 API,那么这样做应该没有问题。

    结论

    有很多方法可以定义和处理输入参数中的参数转换。可能还有其他我没有提到的解决方案。您可能可以通过编写自定义 Absinthe.Phase 来做一些非常疯狂的事情,但这是一项复杂的工作,对于这么简单的事情来说可能过于严厉。

    【讨论】:

      猜你喜欢
      • 2018-12-25
      • 2018-06-09
      • 2018-12-03
      • 2021-07-08
      • 1970-01-01
      • 2020-12-08
      • 2016-02-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多