Cloud Functions で Firebase Dynamic Links を動的に作り、Firestore に入れるとかする

## はじめに
公式ドキュメント [Firebase Dynamic Links Short Links API Reference](https://firebase.google.com/docs/reference/dynamic-links/link-shortener?hl=ja) を参考に、使いまわせる形で Cloud Functions に helper を置いています。
それの紹介 +α です

## Dynamic Links を生成するヘルパー
こういう感じに定義しておき、バックエンド関数やHTTP関数から使います。[^1]

```dynamic_links_helper.ts
import * as functions from 'firebase-functions'
import * as request from 'request-promise-native'

export interface DynamickLinkRequest {
  params: DynamicLinkParameters
}

export interface DynamickLinkRequestResponse {
  shortLink: string
  previewLink: string
}

export interface DynamicLinkParameters {
  domainUriPrefix: string
  link: string
  iosInfo?: {
    iosBundleId?: string
    iosFallbackLink?: string
    iosAppStoreId?: string
  }
  androidInfo?: {
    androidPackageName?: string
    androidFallbackLink?: string
    androidMinPackageVersionCode?: string
  }
  socialMetaTagInfo?: {
    socialTitle?: string
    socialDescription?: string
    socialImageLink?: string
  }
  analyticsInfo?: {
    googlePlayAnalytics: {
      utmSource: string
      utmMedium: string
      utmCampaign: string
      utmTerm: string
      utmContent: string
      gclid: string
    }
    itunesConnectAnalytics: {
      at: string
      ct: string
      mt: string
      pt: string
    }
  }
}

export const createDynamicLink = async (req: DynamickLinkRequest): Promise<DynamickLinkRequestResponse> => {
  const requestParams = {
    dynamicLinkInfo: {
      domainUriPrefix: req.params.domainUriPrefix,
      link: req.params.link,
      iosInfo: req.params.iosInfo,
      androidInfo: req.params.androidInfo,
      socialMetaTagInfo: req.params.socialMetaTagInfo,
      analyticsInfo: req.params.analyticsInfo
    },
    suffix: {
      option: 'SHORT'
    }
  }

  const options: request.Options = {
    uri: `https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=${functions.config().api.key}`,
    method: 'POST',
    headers: {
      'content-type': 'application/json'
    },
    json: JSON.parse(JSON.stringify(requestParams))
  }
  console.info(options)

  await request(options).catch(err => console.error(err))
}
```

## Authentication で User が作られたタイミングで生成する例
前後の文脈は省略しますが、こういう感じで使います。

```index.ts
export const createUserDocument = functions.region('asia-northeast1').auth.user().onCreate(async (snapshot, context) => {
  const functionsConfig = functions.config()
  const response = await createDynamicLink({
    params: {
      domainUriPrefix: functionsConfig.domain.prefix,
      link: `https://${functionsConfig.link.prefix}/user/${user.uid}`,
      iosInfo: {
        iosBundleId: functionsConfig.ios.bundleid
      },
      socialMetaTagInfo: {
        socialTitle: functionsConfig.socialmeta.title,
        socialDescription: functionsConfig.socialmeta.description,
        socialImageLink: functionsConfig.socialmeta.image
      }
    }
  })

  // あとは Firestore のドキュメントにセットするなり色々やる
})
```

### 環境依存の値を管理する
環境依存の値を `functions.config()` で取ってきているのがポイントのです。(See: [Environment configuration](https://firebase.google.com/docs/functions/config-env?hl=en))
ちなみに、cloud functions の config はファイルでのセットには非対応なので、こんな感じでやや強引に対応しています。

```bash
# 値の set
FIREBASE_FLAVOR=development
firebase use $FIREBASE_FLAVOR

# Ref: https://github.com/firebase/firebase-tools/issues/406#issuecomment-367535223
firebase functions:config:set \
$(jq -r 'to_entries[] | [.key, (.value | tojson)] | join("=")' < environments/firebase/functions_${FIREBASE_FLAVOR}.json)

# 値の確認
firebase functions:config:get

# 値の反映
firebase deploy --only functions
```

```environments/firebase/functions_development.json
{
    "link": {
        "prefix": "links"
    },
    "api": {
        "key": "XXX"
    },
    "socialmeta": {
        "image": "https://foo.com/bar.png",
        "title": "Foo",
        "description": "foo"
    },
    "domain": {
        "prefix": "https://foo.com"
    },
    "ios": {
        "bundleid": "com.foo",
        "appStoreId": "XXX"
    },
    "android": {
        "packagename": "com.foo",
        "fallbackLink": "https://foo.com"
    }
}
```

なお、`prefix` の `"links"` は[カスタムドメイン](https://firebase.google.com/docs/dynamic-links)を設定して、Hosting と同じドメインを使っているためです。

```firebase.json
"hosting": {
        "predeploy": "predeploy_hosting.sh",
        "postdeploy": "postdeploy_hosting.sh",
        "public": "hosting",
        "appAssociation": "AUTO",
        "rewrites": [
            { "source": "/links/**", "dynamicLinks": true },
            { "source": "**", "destination": "/index.html" }
        ],
        "ignore": [
            "firebase.json",
            "**/*.sh",
            "**/node_modules/**"
        ]
    }
```

## Firebase の Console からは確認できない
現状、Console では Console から作ったものしか確認できない。
[分析情報は確認できる](https://firebase.google.com/docs/dynamic-links/analytics)が、リンク自体を確認するには [API](https://firebase.google.com/docs/reference/dynamic-links/analytics?hl=en) を使うしかない。
https://firebase.google.com/support からリクエスト投げてみよっと..

[^1]: 参考: [Firebase Dynamic Links Short Links API Reference](https://firebase.google.com/docs/reference/dynamic-links/link-shortener?hl=ja), https://gist.github.com/sgr-ksmt/c0ec70a0a130894d1b7949e010dcbb46