詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

AWS AppSync で時刻のソースをエポックミリ秒として扱いたい

サーバレスで GraphQL したかったので AWS AppSync に入門してみた。スキーマを書いてデータソースとリゾルバを追加して……というフローでずいぶんさくさくと進むんだけど、タイトルに書いたとおり時刻をエポックミリ秒として扱おうとすると困ってしまった。

つまり以下のようなスキーマで、データソースに格納されたエポックミリ秒(例えば 1503932400000)を JSON にして返そうとすると:

type Entry {
  createdAt: Int!
}

こんな感じのエラーになってしまった。

{
  "data": {
    "getEvent": {
      "createdAt": null
    }
  },
  "errors": [
    {
      "path": [
        "getEvent",
        "createdAt"
      ],
      "locations": null,
      "message": "Can't serialize value (/getEvent/createdAt) : Expected type 'Int' but was 'Long'."
    }
  ]
}

なるほどね、と思ったものの GraphQL に Long 型はない。調べてみると、数値が 2147483648 以上になるとリゾルバの内部で Long 型として扱われるように見える。

これは型が Int のフィールドにローカルのリゾルバ(type が None のもの)をアタッチし、request mapping template を

{
    "version": "2017-02-28",
    "payload": 2147483648 ## または 2147483647
}

のようにして確かめることができる。

ミリ秒をあきらめてエポック秒にすればこの境界を越えないわけだけど、せっかくなのでこういう風に解決してみた:

createdAt フィールドが引数を取り、文字列型を返すようにする。

type Entry {
  createdAt(format: String, timezone: String): String!
}

上記 createdAt フィールドにローカルのリゾルバをアタッチし、request mapping template を以下のようにする。

#set($format = $util.defaultIfNull($ctx.args.format, "yyyy-MM-dd HH:mm:ss"))
#set($timezone = $util.defaultIfNull($ctx.args.timezone, "+09:00"))
{
    "version": "2017-02-28",
    "payload": "${util.time.epochMilliSecondsToFormatted($ctx.source.createdAtEpochMillis, $format, $timezone)}"
}

クライアント側からはパーズしやすいフォーマットで受け取るか、受け取った文字列をそのまま表示するなどすればよい。楽しいですね。

参考: Resolver Mapping Template Context Reference - AWS AppSync

Google スライドでプレゼンテーション時にフォントが変わってしまう事象のワークアラウンド

表題の件、みなさまもお困りのことが多いと思います。

日本語のフォントもなんだかダサくなってしまうし。わかりやすい例だと以下のように、スライドの編集時には見えていた絵文字がプレゼンテーションモードでは豆腐になってしまうわけですね。

f:id:motemen:20180420135513g:plain

今回紹介したいワークアラウンドはとても簡単です。

Google スライド拡張をインストールした Chrome でネットワークを切断する。これだけ。

f:id:motemen:20180420135543g:plain

本当にネットワーク切断しなくても、開発者ツールから切断状態にできるので便利ですね。ご査収ください。

パッケージ指定で "go run" する

ちょっとしたコマンドラインツールを作ってるときはよく go run main.go するんだけど、作業ディレクトリが main.go から離れてしまうと go run $GOPATH/src/... みたいなことをする羽目になり、ちょっとありがたくない。ファイルが増えてくると go run *.go することになるが、テストコード(*_test.go)が入ってくると go run できないので、そいつを除いてやる必要もあり面倒。

そこで go list を使ってパッケージ名からソースコードを一覧し、それを go run に渡してやる簡単なシェルスクリプトを書いた。

GitHub - motemen/gorun: Run Go programs by their package path

使い方はこんな感じ。

usage: gorun [-l] [-tags tags] packages [arguments...]

gorun github.com/motemen/ghq みたいに、main パッケージへのパスを指定すると go run 的なことをしてくれます。手元のソースコードを動かしたいなら、go run main.go の代わりに gorun . すればよい。

-l フラグをつけると go run する代わりにソースコードをリストアップしてくれるので、entr みたいなのに渡すのに便利。

実際に go run するところは

go run "${flags[@]}" -exec "bash -c 'shift; exec \"\$0\" \"\$@\"'" "${files[@]}" -- "$@"

となっているんだけど、この -exec がミソで、go run するプログラムの引数に *.go なファイルを渡したいようなときに、プログラムのソースコードとプログラムへの引数を -- によって分割する、というようなことをおこなっている。