Firebaseで入門してみるServerless Forge Apps
はじめに
この記事は、AEC and Related Tech Advent Calendar 2020 の 5 日目の記事です。
本記事では、Autodeskの提供するクラウドサービスである、Autodesk Forgeと、Googleの提供するmBaaSであるFirebaseを組み合わせてサーバレスなWeb APIの構築を実践します。
今回実践するAPIを呼び出すことで、Forge Viewer等、各種Forge APIを呼び出すために必要な認証トークンを取得することができます。
前提
- Forge Appを作成して関連APIを有効化する
- Firebase用のgmailアカウント
- node.jsをインストールしてパスを通す
- vscode等のエディタ
- bash環境
Autodesk Forgeについて
Autodesk ForgeはRevitのrvtファイルを代表とした、プロプライエタリな形式を含んだ様々なデータ形式をWebで扱うことができるクラウドサービスです。Forgeが提供する各種APIを利用することで独自のWeb Appを構築できるプラットフォームとなります。
Forgeは基本的にフロントエンドを持たないバックエンドのサービスとなりますが、クラウドストレージやデータ変換、Webブラウザでの3D Viewerや同社BIM360への透過的なアクセスを利用することができ、成果物のバージョン管理や配布管理、プロジェクトの課題管理やWeb Hooksによる外部サービスとの連携を実現できます。
サーバレスなWebアプリケーション
ForgeでWebアプリケーションを構築する場合、要件によるものの、基本的には利用者に操作画面を提供するためのWeb Appサーバを設置する事になります。一般的な構成でWeb Appサーバを設置する場合、サーバ機器・OS・ミドルウェア等のセットアップや管理をする必要があるので手間が発生してしまいます。
Cloud Functions for Firebaseであればその辺りの煩わしさを回避しつつ、Web APIやフロントエンドを外部に公開できるのでアプリケーションの開発に注力できます。
なお、Firebaseの外へリクエストを飛ばす必要があるので従量課金のBlazeプランを選択する必要がありますが、チュートリアルでお試しで利用する程度であれば無料枠を超える事は無いと思います。
こちらについては、念のため自己責任でよろしくお願いいたします。
Node.js/NPM
Web Appを実装する場合、PythonやRuby等、言語の選択肢には選択の余地がありますが、今回はCloud Functions for Firebaseを利用したいのでJavaScriptのサーバサイド実装であるNode.jsを採用したいと思います。
Cloud Functions for Firebaseではなく、Google Cloud Platform側のCloud FunctionsであればJavaScript以外のランタイムも選択できるので、もし使い慣れた言語がある様ならそちらでも同様の事が可能です。
また、Functionsの実行コードだけでなく、Firebaseの各コンポーネントを管理する上でもNode.js/NPMを前提とするので、開発に必要なランタイムも一元化できるメリットがあります。
Functionsへ実装するAPI
Forge APIは用途に応じてそれぞれにAPIが用意されていますが、今回は認証様のAPIであるAuth APIの機能を実装してみます。
Forge APIは外部からのアクセスをセキュアに実現する為、リクエストには認証済みのトークンを付与する必要があります。例外的にAuth APIは認証トークンを必要とせず、各APIを実行するために必要となるトークンを生成する機能を提供します。
開発環境の準備
Nodeプロジェクトの初期化とfirebase cliのインストール
任意のプロジェクトフォルダに移動して、bashで以下のコマンドを実行します。
# 設定を聞かれるので適当にenterを連打する
npm init
# firebaseのcliをインストールする
npm install --save firebase-tools
firebaseログインとプロジェクトの作成
Firebaseにログインしプロジェクトを新規に作成します。
npx firebase login
コマンドを実行するとブラウザが立ち上がりgmailの選択する画面となりますので、アカウントを選択します。
割り当てる権限スコープが表示されるので「Allow」をクリック
Firebase CLI Login Successfulの画面が表示されたらログインに成功しているので、ターミナルでの作業に戻ります。
firebase projectのワークスペースを作成
npx firebase init
使用するサービス一覧が表示されるので↑
、↓
とスペース
で
- Functions
- Hosting
- Emulators
を選択しEnter
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices.
❯◯ Database: Configure Firebase Realtime Database and deploy rules
◯ Firestore: Deploy rules and create indexes for Firestore
◉ Functions: Configure and deploy Cloud Functions
◉ Hosting: Configure and deploy Firebase Hosting sites
◯ Storage: Deploy Cloud Storage security rules
◉ Emulators: Set up local emulators for Firebase features
◯ Remote Config: Get, deploy, and rollback configurations for Remote Config
Create a new project
を選択してEnter
? Please select an option:
Use an existing project
❯ Create a new project
Add Firebase to an existing Google Cloud Platform project
Don't set up a default project
プロジェクトIDを要求されるので任意のプロジェクトIDを入力してEnter
? Please specify a unique project id (warning: cannot be modified afterward) [6-30 characters]:
Functionsでの使用言語を聞かれるのでJavaScript
を選択してEnter
? What language would you like to use to write Cloud Functions? (Use arrow keys)
❯ JavaScript
TypeScript
ESLintを強制はN
を入力
? Do you want to use ESLint to catch probable bugs and enforce style? (y/N)
Functionsの依存関係を解消するためにY
を入力
? Do you want to install dependencies with npm now? (Y/n)
Hostingの公開フォルダにbuild
を指定
What do you want to use as your public directory? build
SPA向けのrewriteをy
を入力して有効化する
? Configure as a single-page app (rewrite all urls to /index.html)? (y/N)
GitHub Actionsの連携はデフォルトの無効に
? Set up automatic builds and deploys with GitHub? (y/N)
Emulatorsは
- Authentication Emulator
- Functions Emulator
- Hosting Emulator
を選択してEnter
=== Emulators Setup
? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm your choices.
◉ Authentication Emulator
◉ Functions Emulator
◯ Firestore Emulator
◯ Database Emulator
❯◉ Hosting Emulator
◯ Pub/Sub Emulator
なんかいろいろ聞かれるので全てデフォルトの設定のままEnter
? Which port do you want to use for the auth emulator? 9099
? Which port do you want to use for the functions emulator? 5001
? Which port do you want to use for the hosting emulator? 5000
? Would you like to enable the Emulator UI? Yes
? Which port do you want to use for the Emulator UI (leave empty to use any available port)?
? Would you like to download the emulators now? No
i Writing configuration info to firebase.json...
i Writing project information to .firebaserc...
i Writing gitignore file to .gitignore...
✔ Firebase initialization complete!
Forge AuthをGoogle Cloud Functions for Firebaseで実装
functionsにAuthを実装します。
funcionst/index.jsにファイルがあるので下記の内容で上書きします。
functions/index.js
// firebase sdk
const functions = require("firebase-functions");
// forge api sdk
const forge = require('forge-apis');
const express = require("express");
const app = express();
const cors = require("cors")();
// expressのGET:/getTokenに処理を実装する
// web frontendとweb appを異なるoriginにデプロイするのでcorsミドルウェアを挟む
app.get("/getToken", cors, async (req, res) => {
// 環境変数からForge AppのCredentialsを取得する
const clientId = functions.config().forge.client_id;
const clientSecret = functions.config().forge.client_secret;
const scope = ["data:read"];
try {
// Forge SDKのAuthClientTwoLegged()でAuth Clientをインスタンス化
const autoRefresh = true;
const auth = new forge.AuthClientTwoLegged(
clientId,
clientSecret,
scope,
autoRefresh);
// Auth Clientのauthenticate()を実行してトークンを取得する
// authenticate()は非同期処理なのでawaitで実行
const credentials = await auth.authenticate();
return res.status(200).send(credentials);
} catch (err) {
res.status(500).send(err);
}
});
// Functionsのハンドラにexpressを接続。外部APIで呼び出せるようにする
exports.app = functions.region('asia-northeast1').https.onRequest(app);
依存関係の解消
下記のコマンドを実行してFunctionsの外部ライブラリをインストールして依存関係を解消します。
cd functions
npm install --save forge-apis express cors
実行環境の環境変数を設定
Functionsの実行環境にFunctions → Forge Appへアクセスするための接続情報を設定します。
Forge App画面の
- Client ID
- Client Secret
- CallBack URL
を確認しfirebase functions:config:set
で設定します。
npx firebase functions:config:set \
forge.client_id="ここにClient_ID" \
forge.client_secret="ここにClient_Secret" \
forge.redirect_url="http://localhost:3000/callback"
client_id/secretについては、ソースコード内にベタ書きでも動作上問題ないですが、そのままリポジトリにpushした場合は、乗っ取りなどのセキュリティリスクがあるため、環境変数に設定し、ソースコード外部から取得する方法を採ります。
APIをローカルでエミュレーションする
Functionsの環境に設定した値はそのままではローカルエミュレータから参照できないので、functions配下に.runtimeconfig.json
を置きます。
npx firebase functions:config:get > functions/.runtimeconfig.json
.runtimeconfig.json
にはclient_id/secretが格納されているので誤って外部に公開しないよう、リポジトリの管理対象外とするのを推奨します。
runtimeconfigを作成したらローカルエミュレータを実行します。
npx firebase emulators:start --only functions
# emulatorが起動する
i emulators: Starting emulators: functions
⚠ functions: The following emulators are not running, calls to these services from the Functions emulator will affect production: auth, firestore, database, hosting, pubsub
✔ functions: Using node@12 from host.
i ui: downloading ui-v1.3.0.zip...
Progress: ==================================================================================================================================================> (100% of 4MB
i ui: Removing outdated emulator files: ui-v1.1.1
i ui: Removing outdated emulator files: ui-v1.1.1.zip
i ui: Emulator UI logging to ui-debug.log
i functions: Watching "/mnt/c/workspace/20201119_forge/functions" for Cloud Functions...
✔ functions[app]: http function initialized (http://127.0.0.1:5001/dev-serverless-forge-app/asia-northeast1/app).
┌────────────────────────────────────────────────────────────────┐
│ ✔ All emulators ready! View status and logs at 127.0.0.1:4000 │
└────────────────────────────────────────────────────────────────┘
┌───────────┬────────────────┬──────────────────────────┐
│ Emulator │ Host:Port │ View in Emulator UI │
├───────────┼────────────────┼──────────────────────────┤
│ Functions │ 127.0.0.1:5001 │ 127.0.0.1:4000/functions │
└───────────┴────────────────┴──────────────────────────┘
Other reserved ports: 4400, 4500
Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
ローカルエミュレータが起動したらAPIを実行してみます
curl http://127.0.0.1:5001/{FirebaseのプロジェクトID}/asia-northeast1/app/getToken
# tokenが返ってくればok
{"access_token":"xxxxxxxxxx","token_type":"Bearer","expires_in":3599}
エラーが出るようならhttp://127.0.0.4000/logs
をブラウザで開くと動作ログが確認できます。
APIをFirebaseのプロジェクトにデプロイ
ローカルで動いたらFirebaseのプロジェクトにデプロイして外部に公開します。
従量課金を有効化
デプロイ前に課金を有効化する必要があるのでFirebase Consoleをブラウザで開きます。
https://console.firebase.google.com/u/1/
今回新規に作成したプロジェクトのトップへ移動し、プランをSparkからBlazeにアップグレードする。
コンソールからデプロイ
BlazeにアップグレードするとFunctionsをデプロイできるようになるので、コンソールからデプロイを実行します。
npx firebase deploy --only functions
=== Deploying to 'dev-serverless-forge-app'...
i deploying functions
i functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i functions: ensuring required API cloudbuild.googleapis.com is enabled...
✔ functions: required API cloudfunctions.googleapis.com is enabled
✔ functions: required API cloudbuild.googleapis.com is enabled
i functions: preparing functions directory for uploading...
i functions: packaged functions (24.7 KB) for uploading
✔ functions: functions folder uploaded successfully
i functions: creating Node.js 12 function app(asia-northeast1)...
✔ functions[app(asia-northeast1)]: Successful create operation.
Function URL (app): https://asia-northeast1-dev-serverless-forge-app.cloudfunctions.net/app
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/dev-serverless-forge-app/overview
Blazeに更新してからすぐに実行するとエラーが発生してデプロイに失敗する事があるので、少し待ってリトライしてみて下さい。
Webに公開されたAPIを叩いてみる
curl https://asia-northeast1-{FirebaseのプロジェクトID}.cloudfunctions.net/app/getToken
# トークンが返ってくればok
{"access_token":"xxxxx","token_type":"Bearer","expires_in":3599}
これでフロントエンドからWeb APIを通してトークンが取得できるようになりました。
おわりに
今回はAuthトークンの取得までを実践してみましたが、本記事の流れでForge APIをサーバレスなWeb APIとして利用する事ができます。
ほんとはReactと組み合わせたViewerまでの解説をしたかったのですが、残念ながら時間切れとなってしまいました。
また、BIM360やReactを絡めたForgeアプリについては別の記事として上げたいなと思ってはいるのでその際は是非お付き合い頂ければと思います。
(興味ある人いればハンズオンとかもやってみたい…)
内容としてはかなり地味な記事になってしまいましたがご覧頂き有り難うございました!
慌ただしく書き散らしてしまったので、記事の中で検証不足な内容もあるかと思いますが、その際は優しくご指摘頂ければと思います!
Discussion