GraphQLのfieldに付くresolveをTypeによって指定する。

GraphQLでDateTimeを扱う時、フォーマットを指定する。 - あのにのに

前回の記事で 「DateTime型とかはクエリ側で出力フォーマットを引数として指定できると便利!」 って話をしました。

ついでに課題として

  • DateTimeTypeって作ったけど意味なくない..?

  • いちいちresolveにフォーマット付与を記述するの面倒じゃない?

という物があったのでこれを解決したいと思います。

やること

Typeが**ならresolveでxxをする。と言うような仕組みを作ります。

今回の場合だと 「fieldのTypeがDateTimeTypeの場合、そのfieldに引数[:format]を用意し、resolveではformatに応じで出力フォーマット通りに書こうするようにする。」 になります。

これができるようになると、DateTimeTypeが付いたfieldは全て[:format]が使えるようになります。

ちなみに使用するgemは前回に引き続き

https://github.com/rmosolgo/graphql-ruby

です。

やってく

今回はGraphQL::Define::AssignObjectFieldをモンキーパッチをあてる形で動かしてみます。 GraphQL::Define::AssignObjectField#callの程よくfieldが得られたタイミングで

if field.type.to_s == 'DateTime'
  field.arguments['format'] = GraphQL::Argument.define(name: 'format', type: -> { GraphQL::STRING_TYPE })
end

f:id:anoChick:20170201041027p:plain

ついた!

次にリゾルバを作ります。

class DateTimeTypeFormatResolver
  def initialize(attr_name)
    @attr_name = attr_name
  end
  def call(obj, args, ctx)
    return obj[@attr_name].strftime(args[:format]) if args[:format].present?

    obj[@attr_name]
  end
end

こんな感じ。あとは

if field.type.to_s == 'DateTime'
  field.arguments['format'] = GraphQL::Argument.define(name: 'format', type: -> { GraphQL::STRING_TYPE })
  field.resolve=(DateTimeTypeFormatResolver.new(name))
end

とか書けばok

f:id:anoChick:20170201041249p:plain

一応これで完成。

割りと便利な気がする。

今回新たにうまれた課題としては

  • 他でリゾルバを記述したくなったらどうするんだ
  • この書き方だとModelの属性名とTypeのfield名が一致している場合しかつかえない

次↑について考えます。

あとはその他便利拡張あればやる。