DartでCloud Functions for Firebaseを動かす
最近FlutterでDartを触っているので、Dartでなにかできればなと思いました。
ちょっとした処理をしたくなって、Cloud Functions for Firebaseが使えればなと思い調べたところ、DartをJavascriptにトランスパイルしてCloud Functionsで利用できるようにするPluginがあったのでその紹介をします。
firebase_functions_interop
READMEが充実しているのでその通りにやればだいたいできます。
build_runnerでビルドして、生成された.jsファイルをpackage.jsonが見るようにします。
デバッグ
serve
コマンドでローカルに立ち上げることができます。
$ firebase serve --only functions
ハマったところ
通信はnode_httpで行う
http.getを行おうとすると実行時に次のようなエラーが発生します。
ReferenceError: XMLHttpRequest is not defined
firebase_functions_interopがnode_httpで通信を行なっているので、それに合わせます。
Cloud Functions for Firebaseの無料版ではOutbound networkに制限がある
Cloud Functions for Firebase内でSlackなどとOutbound networkを行うと次のようなエラーログが吐かれます
Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions
無料版だとOutbound networkingがGoogle services onlyという制限があります。
解決するにはFlameプランかBlazeプランにする必要があります。
作ったもの
SlackのOutgoing WebHooksを使って、トリガーとなる言葉とユーザグループを指定するとランダムにユーザを選択するものを作りました。
まとめ
DartでもCloud Functions for Firebaseを動かすことはできますが、適宜制約があるのでハマらないように気をつけましょう。
参考
firebase_functions_interop / Dart Package
node-interop/build_node_compilers at master · pulyaevskiy/node-interop / GitHub
Cloud Functions for Firebase - Billing account not configured / Stack Overflow
Flutterで秘匿情報を扱う
最近Flutterを触ってます。
Gitなどに載せたくない情報の扱い方についてのメモ。2つの方法を取り上げます。
.env + flutter_dotenv
flutter_dotenvを使います。pubspec.ymlに以下を追加して
- pubspec.yml
dependencies: .. flutter_dotenv: ^2.0.1 .. flutter: .. assets: - .env
$ flutter pub get
を実行します。
上の例ではプロジェクトルートに .envというファイルを置く想定です。
- .env
SAMPLE_VALUE=this is dot env value.
main関数のはじめに初期化します。
- main.dart
void main() async { await DotEnv().load('.env'); runApp(DotenvApp()); }
次のように呼び出します。
class DotenvApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text("Dotenv App"), ), body: Center( child: Column( children: <Widget>[ Text( 'dotenv values: ${DotEnv().env["SAMPLE_VALUE"]}', ), ], ), ), ), ); } }
Map<String, String>型なので取り出して適宜キャストして利用します。
一度初期化したらどこからでも呼べます。
flutterパッケージに依存しているのでFlutter for Webでは利用できません。
JSON + json_serializable
json_serializableを使います。pubspec.ymlに以下を追加して
- pubspec.yml
dependencies: .. json_annotation: ^2.4.0 .. dev_dependencies: .. build_runner: ^1.0.0 json_serializable: ^3.0.0 .. flutter: .. assets: - secret.json
$ flutter pub get
を実行します。
上の例ではプロジェクトルートに secret.jsonというファイルを置く想定です。
- secret.json
{ "value": "secret value", "num": 20190606 }
JSONは末尾カンマを許してはくれないので注意しましょう
対応するモデルを作成します。
- secret.dart
part 'secret.g.dart'; @JsonSerializable() class Secret { final String value; final int num; const Secret(this.value, this.num); factory Secret.fromJson(Map<String, dynamic> json) => _$SecretFromJson(json); }
このままだと自動生成されたコードがないので、エラーになります。コードを生成します。
$ flutter pub run build_runner build
これでエラーは消えます。
JSONファイルを読み込んでモデルを返すクラスを用意します。
- secret_loader.dart
class SecretLoader { final String secretPath; SecretLoader({this.secretPath}); Future<Secret> load() { return rootBundle.loadStructuredData<Secret>(this.secretPath, (jsonStr) async { final secret = Secret.fromJson(json.decode(jsonStr)); return secret; }); } }
準備はできました。次のように呼び出します。
- secret_app.dart
class SecretApp extends StatelessWidget { final SecretLoader loader = SecretLoader(secretPath: "secret.json"); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text("Secret App"), ), body: FutureBuilder<Secret>( future: loader.load(), builder: (context, snapshot) { if (!snapshot.hasData) { return Center( child: CircularProgressIndicator(), ); } final secret = snapshot.data; return Center( child: Column( children: <Widget>[ Text( 'secret value: ${secret.value}', ), Text( 'secret num: ${secret.num}', ), ], ), ); }, ), ), ); } }
SecretLoader#loadの返り値がFuture型なのでFutureBuilderを使ってます。
内部で保持する仕組みはないので、その他の変数と同じように呼び出したスコープ内でしか扱えません。
flutter_dotenvのときのようにmainで呼び出して、コンストラクタで必要なWidgetに値を渡しても良いと思います。
flutterパッケージに依存していないのでFlutter for Webでも利用できます。その際はJSONファイルをweb/assets/に配置します。
まとめ
どちらも一長一短あります。秘匿情報の利用が初期化時のみで済むのならJSON + json_serializableの方が、若干の準備がありますが良いかなと思いました。構造を持てるのは大きな魅力です。さっと試す程度なら.env + flutter_dotenvのほうが手軽だと思います。
どちらの方法にしても、間違えて秘匿情報を書いたファイルをコミットしないように注意しましょう。
参考
flutter_dotenv / Flutter Package
Storing your secret keys in Flutter – Sócrates Díaz Severino – / Medium
DockerHubでAutomated Buildsを使った時にDocker Tagがうまく設定できない
OpenSSLでファイルの暗号化/復号化をしたくてAlpineベースのDockerイメージを作成しました。
ググったら同じようなものがあったのですが、OpenSSLのバージョンやCA証明書が古かったりで使いにくかったので作成しました。
どのバージョンのOpenSSLが入っているか・または指定できるようにtagをつけて管理したいと思い、Automated Buildsでtagをpushした時に自動でつけられるようにしたかったのですが、ちょっとハマったのでメモ。
何をしたか
デフォルトでmasterブランチを対象に latest
とtagを振ってくれるRULESがあるのですが、今回はtag名にしたかったので不要と思い削除しました。
その上でGitのtagを見てそれをDocker Tagになるように設定したのですが、 Docker Tag
の部分がうまく指定できなくなりました。具体的には、何を入力しても latest
になります。
設定できそうに見えるのですが、保存すると Docker Tag
が latest
に置きかわります。
latest
tagを振るRULESが最低1つは必須なんだと思い、はじめにあったRULESを追加してもダメでした。
どうやら 一番上が優先される ようで、削除して latest
が一番上に来るようにRULESを指定することで思ったように指定できるようになりました。
思えば latest
のtagが振られていないものってないなと思い、それから逸脱するような指定はできないようになっているんだなと納得しました。
まとめ
- RULESは上にあるものが優先される
- latest tagの指定は必要
- 一度tagをつけてAutomated Buildsに成功した後は
latest
tagの指定を削除しても設定できるようになりました。一定の環境下でないと起きないのかもしれません。
- 一度tagをつけてAutomated Buildsに成功した後は