メモ2ブログ

メモtoウェブログ。旧ブログはこちら。 http://sakebook.blogspot.jp/

DartでCloud Functions for Firebaseを動かす

最近FlutterでDartを触っているので、Dartでなにかできればなと思いました。

ちょっとした処理をしたくなって、Cloud Functions for Firebaseが使えればなと思い調べたところ、DartJavascriptにトランスパイルしてCloud Functionsで利用できるようにするPluginがあったのでその紹介をします。

firebase_functions_interop

pub.dev

READMEが充実しているのでその通りにやればだいたいできます。

build_runnerでビルドして、生成された.jsファイルをpackage.jsonが見るようにします。

デバッグ

serve コマンドでローカルに立ち上げることができます。

$ firebase serve --only functions

ハマったところ

通信はnode_httpで行う

http.getを行おうとすると実行時に次のようなエラーが発生します。

ReferenceError: XMLHttpRequest is not defined

firebase_functions_interopnode_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という制限があります。

firebase.google.com

解決するにはFlameプランかBlazeプランにする必要があります。

作ったもの

SlackのOutgoing WebHooksを使って、トリガーとなる言葉とユーザグループを指定するとランダムにユーザを選択するものを作りました。

github.com

まとめ

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関数のはじめに初期化します。

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というファイルを置く想定です。

{
  "value": "secret value",
  "num":  20190606
}

JSONは末尾カンマを許してはくれないので注意しましょう

対応するモデルを作成します。

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;
    });
  }
}

準備はできました。次のように呼び出します。

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

JSON and serialization / Flutter

DockerHubでAutomated Buildsを使った時にDocker Tagがうまく設定できない

OpenSSLでファイルの暗号化/復号化をしたくてAlpineベースのDockerイメージを作成しました。

github.com

ググったら同じようなものがあったのですが、OpenSSLのバージョンやCA証明書が古かったりで使いにくかったので作成しました。

どのバージョンのOpenSSLが入っているか・または指定できるようにtagをつけて管理したいと思い、Automated Buildsでtagをpushした時に自動でつけられるようにしたかったのですが、ちょっとハマったのでメモ。

何をしたか

デフォルトでmasterブランチを対象に latest とtagを振ってくれるRULESがあるのですが、今回はtag名にしたかったので不要と思い削除しました。

f:id:sakebook:20190410012248p:plain
削除したRULES

その上でGitのtagを見てそれをDocker Tagになるように設定したのですが、 Docker Tag の部分がうまく指定できなくなりました。具体的には、何を入力しても latest になります。

f:id:sakebook:20190410012316p:plain
Docker Tagの{sourceref}がlatestに置き換わる

設定できそうに見えるのですが、保存すると Docker Taglatest に置きかわります。

latest tagを振るRULESが最低1つは必須なんだと思い、はじめにあったRULESを追加してもダメでした。

f:id:sakebook:20190410012347p:plain
latestあるけどダメだった

どうやら 一番上が優先される ようで、削除して latest が一番上に来るようにRULESを指定することで思ったように指定できるようになりました。

f:id:sakebook:20190410012413p:plain
設定できた状態

思えば latest のtagが振られていないものってないなと思い、それから逸脱するような指定はできないようになっているんだなと納得しました。

まとめ

  • RULESは上にあるものが優先される
  • latest tagの指定は必要
    • 一度tagをつけてAutomated Buildsに成功した後はlatest tagの指定を削除しても設定できるようになりました。一定の環境下でないと起きないのかもしれません。

参考

Set up Automated builds / Docker Documentation