[React][PowerBI] ReactアプリにPowerBIを埋め込む

# 環境
MacBook Pro (2.3 GHz 8コアIntel Core i9)
macOS 14.0（23A344）
Homebrew 4.3.8
gh 2.52.0

# 目次
- [開発環境を整備する](#開発環境を整備する)
- [Reactプロジェクトを作成する](#reactプロジェクトを作成する)
- [PowerBIレポートを選ぶ](#powerbiレポートを選ぶ)
- [アクセストークンの取得](#アクセストークンの取得)
- [PowerBIレポートをReactアプリに埋め込む](#powerbiレポートをreactアプリに埋め込む)
- [アプリを確認する](#アプリを確認する)
- [ディレクトリ構成](#ディレクトリ構成)

# 開発環境を整備する
## ディレクトリを整備する
1. ディレクトリを作成する
    ~~~sh:terminal
    mkdir powerbi-tutorial
    ~~~

1. ディレクトリに移動する
    ~~~sh:terminal
    cd powerbi-tutorial
    ~~~
## ディレクトリに`create-react-app`をインストールする
1. ディレクトリに移動する
    ~~~sh:terminal
    npm install create-react-app
    ~~~
    or
    ~~~sh:terminal
    npm install -g create-react-app
    ~~~

## ルートディレクトリに`PowerShell`をインストールする
1. ディレクトリに移動する
    ~~~sh:terminal
    cd ~
    ~~~

1. `PowerSHell`を検索する
    ~~~sh:terminal
    brew search powershell
    ~~~

1. `PowwerShell`をインストールする
    ~~~sh:terminal
    brew install powershell/tap/powershel
    ~~~

1. `PowwerShell`を`Terminal`上で動かす
    ~~~sh:terminal
    pwsh
    ~~~
    
# `React`プロジェクトを作成する
1. ディレクトリに移動する
    ~~~sh:terminal
    cd powerbi-tutorial
    ~~~
    
1. `create-react-app`を使用して`react-powerbi-embed`のTypeScritの`React`プロジェクトを作成する
    ~~~sh:terminal
    npx create-react-app react-powerbi-embed --template typescript
    ~~~

1. `react-powerbi-embed`に移動する
    ~~~sh:terminal
    cd react-powerbi-embed
    ~~~

1. `react-powerbi-embed`を起動する
    ~~~sh:terminal
    npm start
    ~~~

    1. `terminal`にport3000の画面が使われている可能性がありと表示された場合、別ポートで起動する[Y]
        ~~~sh:terminal
        ? Something is already running on port 3000. Probably:
        ~~~
        ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/d957541d-3fc0-2aa2-1a5e-d7705fc1a308.png)
    
1. アプリが起動する
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/c03da7b3-96ba-2a59-0e7c-08d3c13d7d36.png)

1. `react-powerbi-embed`のアプリを停止する
    ~~~sh:terminal(起動画面)
    control+z
    or
    control+c
    ~~~
    
1. `powerbi-client-react`ライブラリをインストールする
    ~~~sh:terminal(起動画面)
    npm install powerbi-client-react
    ~~~

# `PowerBI`レポートを選ぶ
1. `アプリ`から`アプリの取得`をクリック
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/c6dcc26c-94c5-3244-ea72-546ad0b588bd.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/631d5e6d-4e18-4b88-b67e-27cb38c1e3d8.png)


1. 下記から、任意のアプリを選ぶ（今回はNFL Analytics by P3を選ぶ）
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/d82cc9a5-508a-ef89-44c4-de8f2f724027.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/f5d4b827-4e26-eb97-71c9-eee09a08a5e5.png)

1. `ワークスペース`から`NFL Analytics by P3`を選ぶ
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/e3fb6317-79c9-cc80-a940-a38e426b36d1.png)

1. `レポート`を選ぶ
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/321c1951-acc0-c615-1e1b-3cdff388a263.png)

1. 開いた`レポート`のURLをメモする（後に使用）※1
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/16b0b373-bc9b-2833-e285-137d409bc829.png)
    ~~~
    レポートURL:<コピーしたレポートのURL>
    ~~~
    ※1:URL内の`https://~/reports/****/~`の`reports/`直後から次の`/`までの`****`をコピー

# アクセストークンの取得
1. `terminal`から`PowerShell`に移動する
    ~~~sh:terminal
    pwsh
    ~~~

1. `PowerShell`を用いてPowerBI用のモジュールを追加する
    ~~~sh:terminal(PowerShell)
    Install-Module MicrosoftPowerBIMgmt
    ~~~

1. `PowerBI`に接続する
    ~~~sh:terminal(PowerShell)
    Connect-PowerBIServiceAccount
    
    // 出力結果
    To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code <code> to authenticate.
    ~~~

1. 出力結果の`URL`と`code`を使ってログインする
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/dc93bfc2-5312-99bd-f63c-c99f8363c3f8.png)

1. アクセストークンを取得する
    ~~~sh:terminal(PowerShell)
    Get-PowerBIAccessToken -AsString
    
    // 出力結果
    Bearer <アクセストークン>
    ~~~

1. アクセストークンをメモする（後に使用）
    ~~~sh:memo
    レポートURL:<コピーしたレポートのURL>
    アクセストークン:<コピーしたアクセストークン>
    ~~~

# `PowerBI`レポートを`React`アプリに埋め込む
1. `react-powerbi-embed`を`VScode`で開く
1. `App.tsx`を開く
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/311d0d4d-3d34-38fd-e7ae-0921d29e5359.png)
    ~~~ts:App.tsx
    import React from 'react';
    import logo from './logo.svg';
    import './App.css';
    
    function App() {
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <p>
              Edit <code>src/App.tsx</code> and save to reload.
            </p>
            <a
              className="App-link"
              href="https://reactjs.org"
              target="_blank"
              rel="noopener noreferrer"
            >
              Learn React
            </a>
          </header>
        </div>
      );
    }
    
    export default App;
    ~~~

1. `import`を追加する
    ~~~ts:App.tsx
    import React from 'react';
    import logo from './logo.svg';
    import './App.css';
    import { PowerBIEmbed } from 'powerbi-client-react';
    import { models, Report} from 'powerbi-client';
    
    function App() {
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <p>
              Edit <code>src/App.tsx</code> and save to reload.
            </p>
            <a
              className="App-link"
              href="https://reactjs.org"
              target="_blank"
              rel="noopener noreferrer"
            >
              Learn React
            </a>
          </header>
        </div>
      );
    }
    
    export default App;
    ~~~

1. 既存の`header`要素の内容を削除して、代わりに[サンプル](https://github.com/microsoft/PowerBI-client-react)を張り付け。（`PowerBIEmbed`は`powerbi-client-react`が提供する`React`コンポーネント。）
    ~~~ts:App.tsx
    import React from 'react';
    import logo from './logo.svg';
    import './App.css';
    import { PowerBIEmbed } from 'powerbi-client-react';
    import { models, Report} from 'powerbi-client';
    
    function App() {
      return (
        <div className="App">
            <PowerBIEmbed
                embedConfig = {{
                    type: 'report',   // Supported types: report, dashboard, tile, visual and qna
                    id: '<Report Id>',
                    embedUrl: '<Embed Url>',
                    accessToken: '<Access Token>',
                    tokenType: models.TokenType.Embed,
                    settings: {
                        panes: {
                            filters: {
                                expanded: false,
                                visible: false
                            }
                        },
                        background: models.BackgroundType.Transparent,
                    }
                }}
            
                eventHandlers = { 
                    new Map([
                        ['loaded', function () {console.log('Report loaded');}],
                        ['rendered', function () {console.log('Report rendered');}],
                        ['error', function (event) {console.log(event.detail);}]
                    ])
                }
            
                cssClassName = { "report-style-class" }
            
                getEmbeddedComponent = { (embeddedReport) => {
                    this.report = embeddedReport as Report;
                }}
            />
        </div>
      );
    }
    
    export default App;
    ~~~

1. `embedConfig`の`id`と`accessToken`に取得した`レポートURL`と`アクセストークン`を張り付ける。また`tokenType`を`models.TokenType.Aad`を指定する
    ~~~ts:App.tsx
    import React from 'react';
    import logo from './logo.svg';
    import './App.css';
    import { PowerBIEmbed } from 'powerbi-client-react';
    import { models, Report} from 'powerbi-client';
    
    function App() {
      return (
        <div className="App">
            <PowerBIEmbed
                embedConfig = {{
                    type: 'report',   // Supported types: report, dashboard, tile, visual and qna
                    id: '<レポートURL>',
                    embedUrl: 'https://app.powerbi.com/reportEmbed',
                    accessToken: '<アクセストークン>',
                    tokenType: models.TokenType.Aad,
                    settings: {
                        panes: {
                            filters: {
                                expanded: false,
                                visible: false
                            }
                        },
                        background: models.BackgroundType.Transparent,
                    }
                }}
            
                eventHandlers = { 
                    new Map([
                        ['loaded', function () {console.log('Report loaded');}],
                        ['rendered', function () {console.log('Report rendered');}],
                        ['error', function (event) {console.log(event.detail);}]
                    ])
                }
            
                cssClassName = { "report-style-class" }
            
                getEmbeddedComponent = { (embeddedReport) => {
                    this.report = embeddedReport as Report;
                }}
            />
        </div>
      );
    }
    
    export default App;
    ~~~


1. `React`ではステート使う事が多いため、`React`の`import`を変更し`useState`を読み込む
    ~~~ts:App.tsx
    import React from 'react';
    ~~~
    ↓
    ~~~ts:App.tsx
    import React, { useState } from 'react';
    ~~~

1. `function`直下にステート管理用のコードを追加する
    ~~~ts:App.tsx
    const [report, setReport] = useState<Report>();
    ~~~
    
    ~~~ts:App.tsx
    import React, { useState } from 'react';
    import logo from './logo.svg';
    import './App.css';
    import { PowerBIEmbed } from 'powerbi-client-react';
    import { models, Report} from 'powerbi-client';
    
    function App() {
      const [report, setReport] = useState<Report>();
      return (
        <div className="App">
            <PowerBIEmbed
                embedConfig = {{
                    type: 'report',   // Supported types: report, dashboard, tile, visual and qna
                    id: '<レポートURL>',
                    embedUrl: 'https://app.powerbi.com/reportEmbed',
                    accessToken: '<アクセストークン>',
                    tokenType: models.TokenType.Aad,
                    settings: {
                        panes: {
                            filters: {
                                expanded: false,
                                visible: false
                            }
                        },
                        background: models.BackgroundType.Transparent,
                    }
                }}
            
                eventHandlers = { 
                    new Map([
                        ['loaded', function () {console.log('Report loaded');}],
                        ['rendered', function () {console.log('Report rendered');}],
                        ['error', function (event) {console.log(event.detail);}]
                    ])
                }
            
                cssClassName = { "report-style-class" }
            
                getEmbeddedComponent = { (embeddedReport) => {
                    this.report = embeddedReport as Report;
                }}
            />
        </div>
      );
    }
    
    export default App;
    ~~~

1. 指定した`setReport`を使うように`getEmbeddedComponent`を変更する
    ~~~ts:App.tsx
    getEmbeddedComponent = { (embeddedReport) => {
    　this.report = embeddedReport as Report;
    }}
    ~~~
    ↓
    ~~~ts:App.tsx
    getEmbeddedComponent = { (embeddedReport) => {
      setReport(embeddedReport as Report);
    }}
    ~~~

1. `cssClassName`に "report-style-class" が指定されているが、そのようなスタイルは存在しないため、`App.css`に以下を追加する
    ~~~css:App.css
    .report-style-class {
        height: 69vh;
        margin: 1% auto;
        width: 60%;
    }
    ~~~

1. サンプルではレポートの背景が透明に指定されているが、見辛いため Default に変更する
    ~~~ts:App.tsx
    background: models.BackgroundType.Transparent,
    ~~~
    ↓
    ~~~ts:App.tsx
    background: models.BackgroundType.Default
    ~~~

1. 保存する

# アプリを確認する
1. `terminal`からアプリを起動する
    ~~~sh:terminal
    npm start
    ~~~

1. `localhost`でアプリが立ち上がることを確認する
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955010/aa7dd5fb-8526-cc93-44ad-a950a88fc8bf.png)


# ディレクトリ構成
~~~:directory_structure
drive/ ←ルートディレクトリ
├── Applications/
├── Syetem/
└── Usr/ ←usr/（隠しファイル）とは異なるファイル
│   ├── shared/
│   └── personnel(ex. Jochun)/　←ホームディレクトリ
│       ├── powerbi-tutorial/
│       │   ├── node_modules/ ←TypeScriptのコンパイラ
│       │   ├── package.json 
│       │   ├── package-lock.json 
│       │   └── reat-powerbi-embed ←Reactプロジェクト
│       ├── jest-tutorial/
│       ├── MyVagrant/
│       ├── Desktop/
│       ├── Downloads/
│       ├── Documents/
│       ├── .config/
│       ├── .ssh/
│       ├── .zshrc/
│       └── .local/
│       │   ├── .bin
│       │   ├── 
~~~



# 参考リンク
## メイン
- [React アプリに Power BI レポートやダッシュボードを埋め込む](https://qiita.com/kenakamu/items/b8dffd24274646dda6b9)
- [React アプリケーションに Power BI 項目を埋め込む](https://learn.microsoft.com/ja-jp/javascript/api/overview/powerbi/powerbi-client-react)

## サンプルコード
- [microsoft/powerbi-client-react](https://github.com/microsoft/PowerBI-client-react)

## `PowerShell`について
- [macOS への PowerShell のインストール](https://learn.microsoft.com/ja-jp/powershell/scripting/install/installing-powershell-on-macos?view=powershell-7.4)

## `PowerBI`のアクセストークンについて
- [Windows PowerShellと PowerShell Core 用の Microsoft Power BI コマンドレット](https://learn.microsoft.com/ja-jp/powershell/power-bi/overview?view=powerbi-ps)
- [Connect-PowerBIServiceAccount](https://learn.microsoft.com/ja-jp/powershell/module/microsoftpowerbimgmt.profile/connect-powerbiserviceaccount?view=powerbi-ps)