assets:precompileでフロントエンドテストがコンパイルされたらtsconfigを疑え

環境

起きたこと

  • jest @testing-library/react あたりを使ってフロントエンドテストを書いた
    • もちろんdevDependenciesで
    • React Componentのテストの拡張子は xxx.test.tsx とした
  • テストファイルは app/javascripts 以下ではなく test ディレクトリを掘って配置した
  • production環境下の assets:precompiletest 以下のファイルもコンパイル対象になり、依存ライブラリを見つけられずにビルドがコケた

対応

webpacker.yml でも config/webpack 以下でもなく tsconfig.json 以下に以下を追加する

  "exclude": [
+    "test",
     "node_modules"
  ]

tsconfigのことすっかり忘れてwebpackerにexclude optionないじゃん〜と困ってしまった。せつない

React hooksのユースケースまとめ

プロダクションでのReact Appの立ち上げ・運用をやっとこ1サイクル回すことができたので、忘れないうちにいくつか知見を書き溜めておこうと思います。
しかし本当に変化が目まぐるしいですね。過去の薄まってしまった記憶を取り戻すためにチュートリアルを一回しして、さーてやっていき〜と思ったところで「クラス型はもうオワコン!これからはhooks!」と言われたときにはどうしようかと思いました(早めに気づけてよかったともいう)。

さて今回はそんなhooksについて。
ベーシックな解説は公式ドキュメントや各種記事に譲るとして、ユースケースに絞ったメモを残しておきます。

useState

ご存知stateですね。値のストアとsetterを提供してくれる、シンプルなhookです。
まあこれはそのまんまなんですが…、主にパラメータの受け渡し時に活躍してくれました。
コンポーネントに値を渡しつつ、onClick / onChangeハンドラでsetXXXして再描画させる、とか。そんな感じです。
モーダルの開閉にも役立ってくれますが、管理するステート量に比例して変数が増えていくのが(見通しの観点で)若干ネック。使い所が違う場合はreducerでまとめるわけにもいかず…。

useReducer

「よくわからん」「useStateとの違いがわからん」などと敬遠されがちなuseReducer。
本来使うべきでない用途で使うと可読性が著しく下がりますね。慣れるまでは「迷ったらstate」でいいと思います。
Formikなどに頼るまでもない、シンプルなフォームオブジェクトを扱う手段*1として使われることが多いかと思いますが、この子が特に輝くのは、前の値を参照してsetしたい場合かなーと。
後述のuseEffectの第2引数として渡す値を事前にクリーニングする、などの用途に役立ってくれました。

useEffect

非同期処理の担い手。正しくは、画面の描画後に1回発火する君。
第2引数に配列を渡した場合は、その中身が変化したタイミングでも再実行されます。
さらに、アンマウント時のクリーンアップ処理を書くこともできる。強力ですね。
バックエンドからデータをフェッチする処理なんかを書いていくと思うんですが、Autopagerのような「値が変わる処理が複数回走りうるユースケース」においてはこちらのような現象を引き起こしがちなので、useStateではなくuseReducerで払い出した変数を第2引数に噛ませておくと暴発せずに済みます。

その他、あるいはReduxについて

useContextとかuseRefとかは、何回か使いかけて結局やめました。
コンポーネントへの伝搬は、多少面倒でも {...props} でドカっと引き渡せちゃう(便利)(濫用注意)こともあって、少なくとも今の規模ではあんまり困っていません。
書き始めでは想定していなかった要件に対応するなどして、分岐や似たような振る舞いをするコンポーネントが増えた場合などに(リファクタの手として)有効な気がしています。こちらについてもまたいずれ、例とともにまとめます。
またReduxの導入ですが、今回は見送ってしまいました。チームにReactチョットワカル人材が(nヶ月前の自分を含め)あまりいない中で、Fluxアーキテクチャまで学習するコストが高くなりすぎることを懸念したためです。塩梅が難しいですね。

*1:objectを定義して引き回す

RDS for MySQL8ではSRSが空になるバージョンがある

  • 8.0.16, 8.0.17では INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS が空の場合がある
  • 8.0.19では修正されているのでバージョンアップするといい

あらまし

Amazon RDS for MySQL(8.0) v8.0.17 にて geometry 型を使おうと思ったところ、うまくスキーマが適用できず
There's no spatial reference system with SRID 4326
SELECT * FROM INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS したら0しか入ってない😫

3月に 8.0.19 がリリースされていたので、試しにバージョンアップしたら無事 SRS の table が作られていました。めでたし。

参考

amazon rds - RDS MySQL 8.0.16 has empty "INFORMATION_SCHEMA.ST_SPATIAL_REFERENCE_SYSTEMS" table - Database Administrators Stack Exchange

WEB+DB PRESS Vol.108にエッセイを寄稿しました

12/22発売の WEB+DB PRESS Vol.108 18周年記念特集「壁の先に見えたもの」にて、「“この先生きのこる”軸足を求めて」というエッセイをお寄せしています。

東京ではすでに先行発売されているとのこと!

f:id:ar_tama:20181220101630j:image

 

審議事案です。


さて、早速ご恵贈いただいた本誌を拝読しているのですが(メイン特集3つがまずとても面白い…!)、特集全体を通してステキな読み物シリーズとなっております。
自分の章を含めいち読者として楽しんで読むことができ、まずは胸を撫で下ろしています。

 

内容ですが、若手、ジュニア層向けの文章を期待されていそうな空気を勝手に察知したので、キャリア1~3年目の皆さまを想定読者に据えて書いてみています。
ちょうど転職の時期と重なったこともあり、自身にとってもキャリアや武器を見つめ直すよい機会となりました。
こういった文章は初めてで非常に難産でしたが…、少しでもどなたかのお役に立てたらとても嬉しいなと思います。ご感想などお待ちしています。

 

他の皆さまの章もすばらしく、laisoさんには勝手に強いシンパシーを感じ、豊田さんの視座の高さに身が引き締まり、藤原さんの項ではその貫禄に平伏しました(語彙が少ない)。

ぜひお手にとってお読みいただけると嬉しいです。なにとぞ!

Azure Functions の breaking changes で派手にシステムがブレイクした話

誰得感しかないんですが備忘のためにも一連の顛末を記しておきます。
結論から言うと、v2(beta)を使っていたのですが派手にぶっ壊れたので泣く泣く v1 に向けて実装しなおしました。

ことの始まり

これ。
Azure Functions Runtime 2.0 breaking changes

詳細を見ても自分のプロジェクトに大きく影響のありそうな変更はなく、休暇中だったこともあり全力でスルーしていました。
日本時間で8/31のお昼すぎに適用されたようです。

環境

  • Azure Functions 2.0(beta)+ Node.js
    • 6 batches (Timer trigger)
    • 7 endpoints (HTTP trigger)
    • 10 workers (Queue trigger)
  • Azure AppService に置いた WebApp から Functions に立てた API を叩いている
beta 使用の動機

Node7以降が使いたかったから。それだけ。それがこんな大事になろうとは……。
async/await と spread operator, util.promisify などが主な動機ですね。

第一報〜一次対応

クライアントから「エラーが出ている」とお知らせがあり確認したところ、たしかに401エラーが出ている。
WebApp からは default キーでアクセスさせていたはずだけど……と見たら、キーの内容が変わっていた。
おそらく "Function App secrets storage change" が原因だろうというのがわかったので、渋々キーを置き換えて再チャレンジ……したらすごい500返ってくるー!

調査

Portal からエラーが出る Function を見にいってみると、なにやら見たことあるエラーが。

Function (xxx) Error: The binding type(s) 'cosmosDB' are not registered. Please ensure the type is correct and the binding extension is installed.

さっきまで動いていたので extension がインストールされていないわけはないのですが……とりあえず、テンプレートから新規に CosmosDB Trigger な Function を作ってみました。
しかし特に新規に extension のインストールを促すダイアログも出ず、エラーも変わらず。
再度変更内容を見ると、host.json の形式が変わったらしい。
JSON のトップレベルに各 extension の設定を書いていたのが、"extensions" の中に入れるようになったようですね。
元々なんの設定もしていなかった(自動で "version": "2.0" のみが入っていた)んですが、例にならって host.json を更新してみる…………

CosmosDBの設定例がない!

詰んだ。
質問のissueが上がっているものの、回答なし。

このあたりで Azure ユーザの先輩の わいとん先生まいんだ先生に泣きつくものの、おふたりともv2を使っておらず派手に散る。
わいとんさんには調査にガッツリお付き合いいただいたものの、ふたりしてウーンわからん、となる。(切り分けなどとても助かりました、本当にありがとうございました)

探しているうちに CLI で最新の extensions が入れられるらしい*1ことがわかり、念のために入れて publish してみたものの状況は変わらず。
いろいろコードをいじってみて、かろうじて documentDB module での CRUD には影響が出ていないことが確認できたので、このあたりで v1 に移植することを考え始める。

にっちもさっちもいかなくなってきたので、一縷の望みをかけて陳情しつつ、他の影響範囲を確認……したところ、host.json で設定したはずの Queue Trigger も動いてないことが判明。もうだめだ!

v1(Node6)移植時にやったこと

1. ばべる
{
  "plugins": [
    "@babel/plugin-transform-async-to-generator",
    "@babel/plugin-proposal-object-rest-spread",
    "babel-plugin-transform-util-promisify"
  ]
}

なお Babel7.0 を使っています。
元のファイルを src ディレクトリ以下につっこんで、commit hook で変換をかけ、元のパスに展開しました。
各 Function の index.js は async style にはせず、batch, endpoint, worker でそれぞれエントリーポイントのフォーマットを揃え、固有ロジックは services ディレクトリに移して変換対象に。*2
これが一番時間がかかった…。

2. 外部ライブラリの変換が必要か調査・対応

ひとつだけ spread operator を使っているライブラリがあったのですが、難しくないものだったのでサクっと自前で実装して完了。

3. bindings の設定変更

CosmosDB まわりの bindings type が 1.0 だと "documentdb" なので、そのあたりを中心に各 function.json を修正。

4. CosmosDB out bindings を JSON.stringify する

これは今回のと直接関係ない気もするんですが…
v1 で動かしたところ、マルチバイト文字の入ったオブジェクトを documentdb:out bindings に渡したら、盛大に文字化けしてしまいました。
理由がわからず悶々としていたのですが、ヤケになって JSON.stringify 噛ませたらなんかうまくいっちゃった、という…。

以上です。とりあえず今は目立った不具合もなく動いてくれているようで、ほっと胸を撫で下ろしています。
事態に進展があったら更新するかも。しないかも。

*1:各 extension の最新バージョンは Nuget に行くとわかる

*2:本当は function の実行に必要なファイル以外は含めるべきではないんですが、ムシムシ

cosmosdb-query-builderというnpmパッケージを作った

www.npmjs.com

node用DocumentDBのライブラリと組み合わせて使うことを想定しています。

動機とか

Win機が手元になくても大丈夫だったり、Lambda と同じノリで書けたりという理由で Azure Functions での開発にはもっぱら node を選択しているのですが、

  1. ありがちな動的に where 句の構成条件が変わるような要件を、文字列操作しまくってなんとかするのは(開発時はよくてもメンテが)なかなかストレスフル
  2. せっかく SQL(的記法)が使えるんならORMまではいかずともクエリビルダーが欲しくなってきた
  3. ざっと探してもなさそうなので、どうせならさいくろん先輩の馴染みのアレみたいな書き方をしたい

というわけで作りました。
そもそも日本で Azure Functions を、しかも Node.js でやっていき人口はだいぶ少ないと思うのですが*1、ありがたいことに多少は npm i されてるっぽいので、必要な人に届いたらいいなあと思います。

DocumentDB は他の RDBMS 達と違って以下のようなしんどめの diff があります:

  • 単一コレクション(テーブル)のクエリでもカラムにコレクション名をつけないといけない
  • LIMIT 句は SELECT とカラムの間に指定する(!?)

主にこの2点と、あとは値のバインドの形が若干煩わしいので、主にそのあたりを解消することを目的としています。

つかいかた/できること

Inspired by CPAN module なので SYNOPSIS とかついてます。

const querySpec = builder.query(
  'SELECT %l %c FROM %t WHERE %w %o',
  20,
  ['id', 'age'],
  'c',
  { id: 200 },
  {
    order: {
      sort: 'desc',
      key: 'createdAt'
    }
  }
);
// SELECT TOP @_top c.id, c.age FROM c WHERE ( c.id = @id ) ORDER BY c.createdAt desc

LIMIT 句を表現するのに、本家のように %o のようなキーワードで吸収できたらいいんですがなかなか…。
また前述の理由で引数にコレクション名を取ります。一応 %t 記法もサポートしたけど今のところあんまり意味ない。

WHERE 句は operator 指定できたり AND/OR のネストができたり関数指定ができたりします。
あとは DocumentDB はストアしている json の型にはめちゃんこ厳しいので、対応するために type 句を取ったりしてます(string 型はちゃんとクオートしないと引けない)。
というわけで地味に便利な仕上がりになっていると思います。

できないこと

現状 JOIN, GROUP 系の記法には対応していません。PRお待ちしてます!

*1:しかし Azure Functions + CosmosDB はベーシックなユースケースでは Lambda + Dynamo より楽で安いと思う。中小企業の味方!なんとベータ版ならNode10も使える!

2017年夏、Apple ATS対応における試行錯誤

ATS(App Transport Security)*1対応にかんして、あれこれ試してみた結果を備忘的に残しておこうという記事です。
なんか延期とか言われてますけどね…
ATSの概要と詳細については以下:
[iOS 9] iOS 9 で追加された App Transport Security の概要 | Developers.IO

TL;DR

やりたいこと
  • 新規アプリで、不特定多数(含 http)のサイトのOGPを表示したい
  • タップで Safari に遷移

これだけ?ええ、これだけです。これだけなんですけど…。

結論

Cocoa Keys/App Store Review for ATS にあるように、
今回のケースは(きっと)

Must connect to a server managed by another entity that does not support secure connections

に該当するはずなので、 NSAllowsArbitraryLoads を立ててレビューをゴリ押しする…のがよさそうですね。

ただ一度は全展開がアナウンスされた機能ですし、レビューで蹴られるポイントを増やすのもなー、ということで、Azure Functionsでさくっとプロキシさせることに*2しました。PaaS最高。
ただこれ、どう考えてもAppleの思惑からは大きくずれているんですよねえ…。

*1:iOS App からwebページを表示させる際に、httpなサイトやAppleの定めた基準を下回るものは弾かれるようになる設定

*2:今回はAPIサーバをAzure Functionsで用意していることもあり

続きを読む