[アップデート] Amazon QuickSight Embedding SDK 2.0.0 がリリースされ、埋め込みダッシュボードのメッセージフックなどカスタムアプリ側で様々なことが出来るようになりました

2023.03.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いわさです。

先日 QuickSight Embedding SDK がメジャーバージョンアップされ、2.0.0 になりました。

今回のアップデートでいくつか新機能が使えるようになっており、ダッシュボードを埋め込んだカスタムアプリケーション側で様々なコントロールが出来るようになっています。

QuickSight Embedding SDK って何?という方もいるかもしれないので、カスタムアプリへの埋め込み方法などをおさらいしつつ、SDK 2.0.0 の新機能を紹介します。

QuickSight をカスタムアプリへ埋め込む

通常は QuickSight のコンソールからアクセスして分析やダッシュボードを作成・閲覧することが多いと思いますが、QuickSight ではオブジェクトを外部 Web ページへ埋め込む機能がサポートされています。(QuickSight Enterprise Edition のみ)

この機能を使うとアプリケーション側で独自にグラフをレンダリングするカスタム処理を実装しなくても QuickSight 上で作成したダッシュボードやビジュアルを埋め込むことでカスタムアプリケーション上へ作成したグラフを表示することが出来ます。

埋め込みを行う方式として、大きく「ワンクリックエンタープライズ埋め込み」と「QuickSight API を使用した埋め込み」がサポートされています。

ワンクリックエンタープライズ埋め込み

ワンクリックエンタープライズ埋め込みはざっくりいうと一時的な認証情報なしで次のような埋め込み URL を作成します。

:
<body>
    <p>
        <h1>hoge quicksight</h1>
    </p>
    <iframe
        width="960"
        height="720"
        src="https://ap-northeast-1.quicksight.aws.amazon.com/sn/embed/share/accounts/123456789012/dashboards/a8b5dee9-0f70-4ce7-856a-9b575ad1f7dd?directory_alias=iwasa-quicksight">
    </iframe>
</body>
:

そのため、カスタムアプリからアクセスするユーザーは QuickSight へのアクセス要求が発生します。
これはカスタムアプリと QuickSight がシングルサインオン統合されており既に認証済みである場合やリーダーセッションキャパシティ機能を使ってパブリックアクセスさせるような場合が想定されています。

この機能について以下の記事で紹介しています。

QuickSight API を使用した埋め込み

QuickSight API を使用した埋め込みの場合は QuickSight API で認証トークンを付与とした短期間有効な URL を発行し、それを iframe で埋め込みます。

:
<div class="text-center">
    <h1 class="display-4">QuickSight Hoge</h1>
    <iframe
        width="960"
        height="720"
        src="https://ap-northeast-1.quicksight.aws.amazon.com/embed/01b649fb828d40de8b325668d396eb41/dashboards/d71ff307-662f-4a2b-9383-499de528300e?code=AYABeBg5P6LsMmkx77jwQLc75pEAAAABAAdhd3Mta21zAFBhcm46YXdzOmttczphcC1ub3J0aGVhc3QtMTozNjcwOTQ1NjE4OTQ6a2V5LzkyZDU3MjEzLTc0MjItNGNhOC1iYWZiLTg2MDFjNGZkODgyNwC4AQIBAHh19lpIdHJLvWmpW7433A8711o_o_2vfnBuxyXbJJdfkwF2PGJFOCHWsSdcC2kRnH3EAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMSNRkTGQAS4dmOlh-AgEQgDsoGR-8BKR50tYR1B4CMXVOA7tdMHfbqAExoaW_6VP8So8Ra1Dq2npbnA79ekQowrbGc4d2RUSdAuf4WQIAAAAADAAAEAAAAAAAAAAAAAAAAACEnFJTDiIhmL5aJiv0-hqw_____wAAAAEAAAAAAAAAAAAAAAEAAACbu-K8IS7WAQUq-mltCRCLzEDuwE51G3_jeJjMkvq-mddE_puxDQnObecs3R9ZIspCsBhWPrXPXSQjRU8iIUmpLUY11XKrnOdNgwV1ZIKb-T_0xOIxYgmzJYXYUCxvjqK8ZRaua2Nf-jqr57tW0kuicLZ561fdvrSzF7QyQXyYZBs4VsUn58G9h7vs2HCQNkPXLpOEN_ktSOipewIav1dRm-JbvrlF66IZi6R7&identityprovider=quicksight&isauthcode=true">
    </iframe>
</div>
:

この機能については以下の記事で紹介しています。

また、埋め込み用の一時 URL の有効期間やセッション期間については以下でも紹介しています。

この埋め込み URL は上記のように iframe で直接埋め込むことも出来るのですが、QuickSight Embedding SDK という JavaScript ライブラリを使うことで、簡単にかつ高度なオプションを利用しながらカスタムアプリへの埋め込みを行うことが出来ます。

SDK 利用時の実装例としては以下のようになります。
※ 最終的に SDK によってブラウザ側でレンダリングされるものは iframe です

:
<head>
    <title>Basic Embed</title>
    <script src="https://unpkg.com/amazon-quicksight-embedding-sdk@1.0.12/dist/quicksight-embedding-js-sdk.min.js"></script>
    <script type="text/javascript">
        var dashboard;

        function embedDashboard() {
            var containerDiv = document.getElementById("embeddingContainer");
            var options = {
                url: "https://ap-northeast-1.quicksight.aws.amazon.com/embed/d3ea2f13f53d4cb0842e3c641e004476/dashboards/17ee7de2-ce26-40dc-9086-bf6d20ad5cad?code=AYABeIlccLb8hLR5I9wKYvl0jS8AAAABAAdhd3Mta21zAFBhcm46YXdzOmttczphcC1ub3J0aGVhc3QtMTozNjcwOTQ1NjE4OTQ6a2V5LzkyZDU3MjEzLTc0MjItNGNhOC1iYWZiLTg2MDFjNGZkODgyNwC4AQIBAHirSky28MTsQkRRkQnrWly9-KCD9GJ1rJU8zazSG85WsgGRXbTrWq16-ZJrZQLMhVeSAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMsUDSJPN5W5huHj2GAgEQgDs-vFHuw-H1RfZKXFFjantjNmjWYOhr9BZEYv1FTanoLY5qWQRpS2ObjDicIZHLJ_OMCWIed5XSNW0U2AIAAAAADAAAEAAAAAAAAAAAAAAAAACceAmAKsaK1Eb7KiSUbZ_M_____wAAAAEAAAAAAAAAAAAAAAEAAADqEK6dgbNT2aX_mbX572Qndejaz6bmHBH9V7MTSN5YCaMNEv9bYFMjZg7KgQMrcdaPeDrs5cjqvn3j2h2WcC2S4qboaPu0F_RIINJcMBCT9a3pRtYbCYTaDCToWpFhlMKf-mF7BVjFOx8eGHvRkdwnjVXGD2rbg33r3BXizT0NZWgLgs38oMGp6AB9ABp85hEtIBpGY2QXOG85MJoLxfqyR883YQ8uW2eOpmCAfMjHFmBgzHC3V9tGimKPB0G4QmNxrSvIn3zlrkdn0bo6dWY0zseHGRpBhI3lP5RRSTnlzpT636UvFH_4mBOcEut-Ai7fR2EUUpekVxIoSQ%3D%3D&identityprovider=quicksight&isauthcode=true",  
                container: containerDiv,
                scrolling: "no",
                height: "700px",
                width: "1000px",
                footerPaddingEnabled: true
            };
            dashboard = QuickSightEmbedding.embedDashboard(options);
        }
    </script>
</head>

<body onload="embedDashboard()">
    <div id="embeddingContainer"></div>
</body>
:

この機能については以下の記事で紹介しています。

SDK 2.0.0 の新機能を使ってみる

今回新たに TypeScript がサポートされていたりパラメータが分割されていたりはするのですが、SDK 自体の使い方は大きくは変わりません。
そこで、今回は新機能をいくつか試してみます。

今はダッシュボード内の特定ビジュアルのみを埋め込むことが出来るので、以下の記事で作成したダッシュボードの上部のビジュアルをカスタムアプリに埋め込んでみます。

サーバーサイドでの実装はせずに簡易的に AWS CLI を使って埋め込み用 URL を取得します。

% cat hoge.json                                                                                                          
{
    "AwsAccountId": "123456789012",
    "UserArn": "arn:aws:quicksight:ap-northeast-1:123456789012:user/default/hoge-iwasa/hoge-iwasa",
    "ExperienceConfiguration": {
        "DashboardVisual": {
            "InitialDashboardVisualId": {
                "DashboardId": "97d23333-757b-4754-80c8-ccf9b6055d98",
                "SheetId": "97d23333-757b-4754-80c8-ccf9b6055d98_33fc95f8-7b8e-4d30-9a2b-480f59afdbc6",
                "VisualId": "97d23333-757b-4754-80c8-ccf9b6055d98_e519fa7d-657e-46cf-906d-a43854f19b66"
            }
        }
    },
    "AllowedDomains": [
        "http://localhost:8080"
    ]
}

% aws-v1 quicksight generate-embed-url-for-registered-user --cli-input-json file://hoge.json                             
{
    "Status": 200,
    "EmbedUrl": "https://ap-northeast-1.quicksight.aws.amazon.com/embed/44348dc326eb47bda9a0b906ac65cb7d/dashboards/97d23333-757b-4754-80c8-ccf9b6055d98/sheets/97d23333-757b-4754-80c8-ccf9b6055d98_33fc95f8-7b8e-4d30-9a2b-480f59afdbc6/visuals/97d23333-757b-4754-80c8-ccf9b6055d98_e519fa7d-657e-46cf-906d-a43854f19b66?code=AYABeMhdt0eB3y2dEFfvexiFr3sAAAABAAdhd3Mta21zAFBhcm46YXdzOmttczphcC1ub3J0aGVhc3QtMTozNjcwOTQ1NjE4OTQ6a2V5LzkyZDU3MjEzLTc0MjItNGNhOC1iYWZiLTg2MDFjNGZkODgyNwC4AQIBAHgeDGL3XAnX5chwrubXLtqmn-xwPT7Th-AyeZ72nfHdvwFg1vjWSUJG2N5etCpmtyW4AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMK2ZaubTPvwejY4MdAgEQgDvVU-LsrXzdT9hfIaJrQOHrkEnZIRuD2OSZgN2jwwLpADXO1dqtquTxw8AzHo7NXBbUItWsmbikv-XgMQIAAAAADAAAEAAAAAAAAAAAAAAAAAC0cST8-Mj7egDZsbGteqJ9_____wAAAAEAAAAAAAAAAAAAAAEAAACb4j3N-mrxxqXFnpWCK_cJoiFpuAtLN3Rjdcics1uQsCF8nt-ahpedV43qK_dPwNZFXC-n8Wf6_c_a8l9qAXZHfJYUIsBlu5rymPBLq6o_qFJIg1Y1GJBLH0rBLjCtwhqVsLX_zMAbyhhu5EPNAhMubK9KSijMdTvataGbgVuV5eyE7SRet7Oub5KoWHIq5FRjXOR9-e0YJXsoJqfTJtdweo7SMRxqUZ_o3YPj&identityprovider=quicksight&isauthcode=true",
    "RequestId": "5673e67d-64a7-41ee-8e03-fb4ddf13870c"
}

取得した埋め込み URL を使って次のような静的 HTML を作成します(5 分以内)
新機能については後ほど解説します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <title>Document</title>
    <script src="https://unpkg.com/amazon-quicksight-embedding-sdk@2.0.0/dist/quicksight-embedding-js-sdk.min.js"></script>
    <script type="text/javascript">
        const onloadDashboard = async() => {    
            const {
                createEmbeddingContext,
            } = QuickSightEmbedding;

            const embeddingContext = await createEmbeddingContext({
                onChange: (changeEvent, metadata) => {
                    console.log('Context received a change', changeEvent, metadata);
                },
            });

            const frameOptions = {
                url: "https://ap-northeast-1.quicksight.aws.amazon.com/embed/1c4fbf71c1ee437aa6730e88c0981d38/dashboards/97d23333-757b-4754-80c8-ccf9b6055d98/sheets/97d23333-757b-4754-80c8-ccf9b6055d98_33fc95f8-7b8e-4d30-9a2b-480f59afdbc6/visuals/97d23333-757b-4754-80c8-ccf9b6055d98_e519fa7d-657e-46cf-906d-a43854f19b66?code=AYABeEepucbg9sKfiIWc5zo0LTQAAAABAAdhd3Mta21zAFBhcm46YXdzOmttczphcC1ub3J0aGVhc3QtMTozNjcwOTQ1NjE4OTQ6a2V5LzkyZDU3MjEzLTc0MjItNGNhOC1iYWZiLTg2MDFjNGZkODgyNwC4AQIBAHgeDGL3XAnX5chwrubXLtqmn-xwPT7Th-AyeZ72nfHdvwGQuonWqYA_zktoPjV0f8EGAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMiy0u-ztrhGq7q0dwAgEQgDvbIlf0ahJXa2aMYx54gcCS__tXACV8dQROqItZh8x0pIeMDKGfDqQCX6NTw-x2ItrVLkHPfb7JrtiBHgIAAAAADAAAEAAAAAAAAAAAAAAAAAAnJVUtNCg4RvzDOZsf88ch_____wAAAAEAAAAAAAAAAAAAAAEAAACb-otFT4VCoIUiEx0nIEYGfJX9sjYfkfqwGkAd8fB8QhPd-nsjBH2fVhwMiTF3An64vguxDpM8b5zZMXkHSJkDjPdrpg0z0PUWFsq8rod3JrFyg1FF9WPMvjP4AD7tM1LhZT1S08DGYzP5kdda55j6-GtuZbhGDmze9zXM5E6F3XOs-9D0EdUJCbB38GX4tW2vBt-_LUnV0P7bTjYB_ntzk1gR9qIoUdLry--D&identityprovider=quicksight&isauthcode=true",
                container: '#quicksight-container',
                height: "300px",
                width: "300px",
                withIframePlaceholder: true,
                onChange: (changeEvent, metadata) => {
                    switch (changeEvent.eventName) {
                        case 'FRAME_MOUNTED': {
                            console.log("Do something when the experience frame is mounted.");
                            break;
                        }
                        case 'FRAME_LOADED': {
                            console.log("Do something when the experience frame is loaded.");
                            break;
                        }
                    }
                },
            };

            const contentOptions = {
                locale: "ja-JP",
                parameters: [
                    {
                        Name: 'targetuser',
                        Values: ['user1'],
                    }
                ],
                toolbarOptions: {
                    undoRedo: true,
                    reset: true
                },
                onMessage: async (messageEvent, experienceMetadata) => {
                    switch (messageEvent.eventName) {
                        case 'CONTENT_LOADED': {
                            console.log("Received when the visuals of the Amazon QuickSight dashboard are fully loaded");
                            break;
                        }
                        case 'ERROR_OCCURRED': {
                            console.log("Received when an error occurred while rendering the visuals of the Amazon QuickSight dashboard. The message contains `errorCode`. ");
                            break;
                        }
                        case 'PARAMETERS_CHANGED': {
                            console.log("Received when the parameters in Amazon QuickSight dashboard changes.");
                            break;
                        }
                        case 'SELECTED_SHEET_CHANGED': {
                            console.log("Received when the selected sheet in Amazon QuickSight dashboard changes.");
                            break;
                        }
                        case 'SIZE_CHANGED': {
                            console.log("Received when the size of the Amazon QuickSight dashboard changes.");
                            break;
                        }
                        case 'MODAL_OPENED': {
                            console.log("Received when a modal opened in Amazon QuickSight dashboard.");
                            break;
                        }
                    }
                },
            };
            const embeddedVisualExperience = await embeddingContext.embedVisual(frameOptions, contentOptions);

            const selectUserElement = document.getElementById('user');
            selectUserElement.addEventListener('change', (event) => {
                console.log("select user changed.");
                embeddedVisualExperience.setParameters([
                    {
                        Name: 'targetuser',
                        Values: event.target.value
                    }
                ]);
            });

            const btnUndoElement = document.getElementById('btnReset');
            btnUndoElement.addEventListener('click', (event) => {
                console.log("reset.");
                embeddedVisualExperience.reset();
            });
        };
    </script>
</head>
<body onload="onloadDashboard()">
    <select id="user" name="user">
        <option value="user1">Hoge User1</option>
        <option value="user2">Hoge User2</option>
        <option value="user3">Hoge User3</option>
    </select>
    <input id="btnReset" type="button" value="reset" />
    <div id="quicksight-container"></div>
</body>
</html>

なんでも良いので適当な Web サーバーで起動します。
ポートは先程埋め込み URL 取得時に指定したポートを使います。許可されたドメインでのみ埋め込み URL は利用が可能です。

% python -m http.server 8080                                                                
Serving HTTP on :: port 8080 (http://[::]:8080/) ...

メッセージ受信

今回大きな機能として contentOptions で onMessage 経由で埋め込みオブジェクト側からのメッセージをカスタムアプリで受信することが出来るようになっています。
今までも onChange など一部イベントを受信することは出来ていましたが、より詳細な情報やパラメータを受け取ることが出来るようになっています。

これによって QuickSight とカスタムアプリ間で双方向に様々な同期などを行うことが出来るようになりそうです。

Undo/Redo や Reset アクション

カスタムアプリから埋め込みオブジェクトに対してアクション機能を使って Undo/Redo や Reset を行うことが出来るようになりました。

ダッシュボード埋め込みの場合だと、QuickSight 側の Reset ボタンなどを表示することが出来ていたのですが、ビジュアル埋め込みなどの場合にこの機能を使って Reset 操作などを送信することが出来ます。

さいごに

本日は Amazon QuickSight Embedding SDK 2.0.0 がリリースされたので紹介しました。
SDK のリリース通知は以下から確認出来ます。

半年ほど前にビジュアルだけを外部埋め込めるようになりましたが、今回の新機能やパラメータ送信機能などを組み合わせると、より外部アプリに組み込む際にカスタマイズしやすくなったのではないでしょうか。

カスタムアプリ側で自前で描画するのも悪くないですが、ダッシュボードの実装やメンテナンスをカスタムアプリから分離出来たり、負荷の高い分析が必要な際のオフロードも出来るので QuickSight の埋め込み機能も検討してみてください。