Looker 可視化コンポーネントを使ってみる

iframe による Looker のコンテンツの埋め込みは、デベロッパーがウェブ アプリケーションにダッシュボード、Look、Explore を追加する際に使用できる方法の 1 つにすぎません。このチュートリアルでは、デベロッパーが React アプリに Looker 可視化を追加する別の方法を紹介します。このチュートリアルは、シンプルな React アプリの作成の開始条件に基づいており、Looker 可視化コンポーネントを使用します。

このチュートリアルで扱う手順は次のとおりです。

  1. Looker からクエリスラッグを取得する
  2. Looker の可視化コンポーネントを使用して React アプリケーションを作成する
  3. バックエンド ヘルパー サービスを作成する

Looker からクエリスラッグを取得する

React アプリはこれらの手順に依存しているため、Looker で行う必要があることがいくつかあります。

クエリスラッグを取得する

可視化コンポーネントの支柱として使用するクエリ ID またはスラッグが必要です。この記事では、Explore URL からクエリスラッグを取得する方法について説明します。別の例については、Looker のドキュメントをご覧ください。

Looker インスタンスで CORS を構成する

クロスオリジン リソース シェアリング(CORS)は、埋め込みと同じドメイン許可リストによって制御されます。

詳細については、署名付き埋め込みのドキュメント ページをご覧ください。

  1. Looker インスタンスで [管理者] > [プラットフォーム埋め込み] に移動します。これには管理者権限が必要です。
  2. React アプリは、デフォルトでは http://localhost:3000 で実行されます。このアドレスを埋め込みドメイン許可リストに追加すると、アプリからのリクエストを許可し、同じアドレスでリクエストに応答するよう Looker に指示できます。アプリが Looker インスタンスに API リクエストを行うため、このステップは必須です。それ以外の場合は、Looker とアプリ間の通信は行われません。

React アプリケーションを作成する

このデモのフロントエンドは、React App の作成を使用して、単一ページの React アプリケーションを作成します。デモのルートフォルダ(get-started-viz-components)で次のコマンドを実行して、アプリを作成し、依存関係をインストールします。

npx create-react-app frontend-react cd frontend-react npm i
@looker/visualizations npm i @looker/components @looker/components-data
styled-components

これらのコマンドを実行すると、フォルダ構造は次のようになります。

Node モジュール、Public、src フォルダを含む Frontend というフォルダがあり、ファイルは .gitignore、package-lock.json、package.json を呼び出します。

package.json ファイルを調べて react-dom もインストールされていることを確認します。インストールされていない場合は、npm i react-dom を実行してインストールします。

このデモの package.json は次のようになります。

{
  "name": "frontend-react",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@looker/components": "^4.0.3",
    "@looker/components-data": "^1.0.0",
    "@looker/sdk": "^22.16.0",
    "@looker/sdk-rtl": "^21.4.0",
    "@looker/visualizations": "^1.1.1",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^12.1.0",
    "@testing-library/user-event": "^12.4.0",
    "i": "^0.3.7",
    "npm": "^8.19.2",
    "react": "^16.14.0",
    "react-dom": "^16.14.0",
    "react-scripts": "5.0.1",
    "styled-components": "^5.3.6",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

環境変数を構成する

アプリのルート ディレクトリ(./frontend-react)に .env ファイルを作成し、次の変数を設定します。

REACT_APP_LOOKER_API_HOST=https://your-looker-instance.looker.com
REACT_APP_BACKEND_SERVER=http://localhost:3001/

REACT_APP_BACKEND_SERVER は、アクセス トークンを抽出する Looker への API 呼び出しを行うために使用するバックエンド ヘルパー サービスのアドレスです。

REACT_APP_LOOKER_API_HOST は、React アプリから API リクエストを受け取る Looker インスタンスのアドレスです。

クライアント サイド SDK を初期化する

React アプリは SDK を使用して、Looker サーバーに API リクエストを行います。これはフロントエンドで行うため、次のヘルパーを使用して sdk を初期化できます。

import { Looker40SDK } from '@looker/sdk'
import {
  AuthToken,
  AuthSession,
  BrowserTransport,
  DefaultSettings,
} from '@looker/sdk-rtl'

class SDKSession extends AuthSession {
  // This is a placeholder for the fetchToken function.
  // It is modified to make it useful later.
  async fetchToken() {
    return fetch('')
  }

  activeToken = new AuthToken()
  constructor(settings, transport) {
    super(settings, transport || new BrowserTransport(settings))
  }

  // This function checks to see if the user is already authenticated
  isAuthenticated() {
    const token = this.activeToken
    if (!(token && token.access_token)) return false
    return token.isActive()
  }

  // This function gets the current token or fetches a new one if necessary
  async getToken() {
    if (!this.isAuthenticated()) {
      const token = await this.fetchToken()
      const res = await token.json()
      this.activeToken.setToken(res.user_token)
    }
    return this.activeToken
  }

  // This function authenticates a user, which involves getting a new token
  // It returns a modified object with a new authorization header.
  async authenticate(props) {
    const token = await this.getToken()
    if (token && token.access_token) {
      props.mode = 'cors'
      delete props.credentials
      props.headers = {
        ...props.headers,
        Authorization: `Bearer ${this.activeToken.access_token}`,
      }
    }
    return props
  }
}

// This class sets the fetchToken to use the 'real' address of the backend server.
class SDKSessionEmbed extends SDKSession {
  async fetchToken() {
    return fetch(`${process.env.REACT_APP_BACKEND_SERVER}`)
  }
}

// This creates a new session with the 'real' address used above
const session = new SDKSessionEmbed({
  ...DefaultSettings,
  base_url: process.env.REACT_APP_LOOKER_API_HOST,
})

// This exports the SDK with the authenticated session
export const sdk = new Looker40SDK(session)

可視化をアプリに埋め込む

可視化のクエリスラッグ(この例では Jlm4YHPeT3lLGA9UtHjZcA)が完了し、sdk オブジェクトがインスタンス化されました。次のステップでは、Looker 可視化コンポーネントを使用して可視化をアプリに埋め込みます。

import { sdk } from '../src/helpers/CorsSession'
import { Query, Visualization } from '@looker/visualizations'
import { DataProvider } from '@looker/components-data'
import { ComponentsProvider } from '@looker/components'

function App() {
  return (
    <>
      <h1>Get started with Looker visualization components</h1>
      <ComponentsProvider>
        <DataProvider sdk={sdk}>
          {/* Change this query slug to match your query slug */}
          <Query query="Jlm4YHPeT3lLGA9UtHjZcA">
            <Visualization />
          </Query>
        </DataProvider>
      </ComponentsProvider>
    </>
  )
}

export default App

フロントエンドの準備が完了しました。コンポーネントの追加やアプリのスタイル追加など、さまざまなことが可能です。

バックエンド ヘルパー サービスを作成する

最後のステップは、フロントエンドからの呼び出しを受信するバックエンド ヘルパー サービスを構築し、Looker-Node SDK を使用してユーザーを認証し、アクセス トークンを抽出してフロントエンドに送り返します。

説明を簡単にするために、1 つのエンドポイントを持つノードサーバーを構築します。サーバーは expresscors@looker/sdk-node の依存関係を使用します。ルートフォルダ(get-started-viz-components)から次のコマンドを実行できます。

mkdir backend-node
cd backend-node
npm init -y
npm i express cors @looker/sdk-node

バックエンドから SDK を認証するには、looker.ini ファイルを使用します。ファイルにデータを入力する方法について詳しくは、SDK-Node のページをご覧ください。これらのコマンドを実行すると、フォルダ構造は次のようになります。

node_modules というフォルダと、looker.ini、package-lock.json、package.json、server.js というファイルが入っている backend-node というフォルダ。

package.json は次のようになります。

{
  "name": "looker-embed-backend",
  "version": "1.0.0",
  "description": "Backend helper service for getting started with Looker Viz components",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "author": "Looker",
  "license": "Apache-2.0",
  "dependencies": {
    "@looker/sdk-node": "^22.16.0",
    "cors": "^2.8.5",
    "express": "^4.18.2"
  }
}

次に、このコードを新しい server.js ファイルに追加します。

const cors = require('cors')
const express = require('express')
const { LookerNodeSDK } = require('@looker/sdk-node')

const port = 3001
const app = express()
// The init40 method below will authenticate using
// the looker.ini file
const sdk = LookerNodeSDK.init40()

app.use(
  cors({
    origin: '*',
  })
)
app.use(express.json())

app.get('/', async (req, res) => {
  const userId = await sdk.ok(sdk.me('id'))
  const accessToken = await sdk.login_user(userId.id)
  const user = {
    user_token: accessToken.value,
    token_last_refreshed: Date.now(),
  }
  res.json({ ...user })
})

app.listen(port, async () => {
  console.log(`Backend Server listening on port ${port}`)
})

サーバーと React アプリを起動する

  • ターミナルを開いて、backend-node フォルダに移動し、npm start を実行します。
  • 2 つ目のターミナルを開き、frontend-react フォルダに移動して npm start を実行します
  • バックエンド ヘルパー サービスと React アプリが稼働したら、ブラウザを開いて http://localhost:3000/ に移動し、アプリケーションに埋め込まれた可視化を確認します。

GitHub でコードを表示する