⛰️

Firebaseで入門してみるServerless Forge Apps

2020/12/05に公開

はじめに

この記事は、AEC and Related Tech Advent Calendar 2020 の 5 日目の記事です。

本記事では、Autodeskの提供するクラウドサービスである、Autodesk Forgeと、Googleの提供するmBaaSであるFirebaseを組み合わせてサーバレスなWeb APIの構築を実践します。

今回実践するAPIを呼び出すことで、Forge Viewer等、各種Forge APIを呼び出すために必要な認証トークンを取得することができます。

3Dビューア

前提

  • 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による外部サービスとの連携を実現できます。

Forge App

サーバレスな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の選択する画面となりますので、アカウントを選択します。

OAuthの画面

割り当てる権限スコープが表示されるので「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で設定します。

ClientID/ClientSecret

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をブラウザで開くと動作ログが確認できます。

emulators ui

APIをFirebaseのプロジェクトにデプロイ

ローカルで動いたらFirebaseのプロジェクトにデプロイして外部に公開します。

従量課金を有効化

デプロイ前に課金を有効化する必要があるのでFirebase Consoleをブラウザで開きます。

https://console.firebase.google.com/u/1/

firebase console

今回新規に作成したプロジェクトのトップへ移動し、プランを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