AWS QuickSightSDKのGenerateEmbedUrlForRegisteredUserで埋め込みURLを発行し、グラフを表示させる



# はじめに
この記事はシーエー・アドバンス9日目の記事です！

今回はバックエンドに利用しているrailsから、QuickSightSDKの[GenerateEmbedUrlForRegisteredUser](https://docs.aws.amazon.com/ja_jp/quicksight/latest/APIReference/API_GenerateEmbedUrlForRegisteredUser.html)を利用して、QuickSightの埋め込み用URLを発行し画面にグラフ(ダッシュボード)を表示させてみます。
QuickSight自体、グラフのURLを全体公開にしてサイトに埋め込むことはできるのですが、それではURLさえわかれば誰でもグラフを見れる状態でよくないので、埋め込みURLを発行して表示させる方法をとります。

## 前提条件
  - AWSアカウントを所持していること
  - QuickSightでアカウント登録していること
    - 24$/月ぐらいかかります
      - [料金 - Amazon QuickSight \| AWS](https://aws.amazon.com/jp/quicksight/pricing/)
    - 9＄/月のSTANDARD EDITIONでは動作確認してないです
    - 登録時ユーザー作成方法を選択でき、既存IAMユーザーを利用してQuickSightアカウント作成することもできます。
  - QuickSight内でダッシュボードを公開していること

ダッシュボードの公開方法は、分析の作成後右上の公開ボタンを押して、新しいダッシュボードとしてダッシュボードを公開してください。
![分析 2023-12-09 5.09.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264617/f2b8eed0-6573-9bfe-f87e-b75865deb339.png)

その後ダッシュボードの画面が開かれます。URLを確認すると
`https://ap-northeast-1.quicksight.aws.amazon.com/sn/dashboards/"英数字で書かれたダッシュボードID"`になっています。
後ほどこのダッシュボードIDを利用するので、メモしておくか再度ダッシュボードを開いて確認してください。
![ダッシュボード 2023-12-09 5.10.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264617/192d0435-70ff-577f-1af9-8fdd71a69120.png)


## 実行環境
  - バックエンド
    - ruby 3.2.2
    - rails6.1
    - aws-sdk-quicksight 1.79.0
  - フロント
    - Angular v15
    - Typescript 4.8.4
    - node v18.18.0

# 実装
実装するにあたってrailsのAPIサーバーとなんでもいいので表示用のフロントを用意します。
また今回の[登録済みユーザー向けの埋め込みURL発行](https://docs.aws.amazon.com/ja_jp/quicksight/latest/user/embedded-analytics-dashboards-for-authenticated-users.html)には、QuickSightに登録済みのIAMユーザーが必要なため、その準備も行います。
## IAMユーザー

QuickSightアカウント登録したIAMユーザーのポリシーに、以下を追記してください。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "quicksight:RegisterUser",
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": "quicksight:DescribeUser",
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "quicksight:GenerateEmbedUrlForRegisteredUser"
            ],
            "Resource": "arn:aws:quicksight:<YOUR_AWS_ACCOUNTID>:user/${aws:userid}"
            "Effect": "Allow",
        }
    ]
}
```
詳細に設定したい場合は、[こちらのドキュメント](https://docs.aws.amazon.com/ja_jp/quicksight/latest/user/iam-policy-examples.html)を参考に設定してください。

## rails
### gem追加
rails環境にgemを追加します
```sh
gem install aws-sdk-quicksight
```
### 定数設定
インストール後settings.yml内部でregionやaccess_keyなどの定数を設定します。
漏れちゃいけないkeyは.envなどで管理しておくと良いです。
```yml
#settings.yml
quicksight:
  region: "ap-northeast-1"
  aws_account_id: "account_id"
  session_lifetime_in_minutes: 15
  dashboard_id: "表示させたいダッシュボードID"
  user_arn: "arn:aws:quicksight:<YOUR_AWS_ACCOUNTID>:user/${aws:userid}"
  access_key_id: <%=ENV['ACCESS_KEY'] %>
  secret_access_key: <%=ENV['SECRET_ACCESS_KEY'] %>
  allowed_domains:
    - http://localhost:4200(許可したいドメイン)
```

### module追記
lib配下のmodule内にclassとqsclientを定義して呼び出せるようにします。
```ruby
  class Aws
    @qsclient = nil
    def self.qsclient
      return @qsclient if @qsclient.present?
      @qsclient = ::Aws::QuickSight::Client.new(region: Settings.quicksight.region, access_key_id: Settings.quicksight.access_key_id, secret_access_key: Settings.quicksight.secret_access_key)
      @qsclient
    end
  end
```

### controller
```ruby
# QuickSightsController.rb
module V1
  class QuickSightsController < ApplicationController
    before_action :set_dashbord_options

    def graph
      @options[:experience_configuration][:dashboard][:initial_dashboard_id] = Settings.quicksight.dashboard_id

      response = qsclient.generate_embed_url_for_registered_user(@options)

      render json: response, status: :ok
    end

    private

      def qsclient
        Mpv::Aws.qsclient
      end

      def set_dashbord_options
        @options = {
          aws_account_id: Settings.quicksight.aws_account_id,
          allowed_domains: Settings.quicksight.allowed_domains,
          session_lifetime_in_minutes: Settings.quicksight.session_lifetime_in_minutes,
          user_arn: Settings.quicksight.user_arn,
          experience_configuration: { 
            dashboard: {
              initial_dashboard_id:'',
              feature_configurations: {
                state_persistence: {
                  enabled: false,
                },
                bookmarks: {
                  enabled: false,
                },
              },
            },
          },
        }
      end
  end
end

```
### ルーティング設定
```ruby
# routes.rb
get 'quicksights/graph', to: 'quicksights#graph'
```

## 画面
今回はAngularとTypeScriptを利用しているため、それに合わせてmodelとservice, componentを作成します。

### QuickSight用のmodel
```typescript
export interface QuickSight {
  embed_url: string;
  status: number;
  request_id: string;
}

```
### QuickSight用のservice
```typescript
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { QuickSight } from 'Models/custom/QuickSight';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class QuickSightAccessService {
  constructor(private http: HttpClient) {}

  getEmbedUrl = (): Observable<QuickSight> => this.http.get<QuickSight>(`api/v1/quicksights/graph`);
}
```

### 埋め込みグラフ表示用のcomponent
```typescript
import { Component } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { QuickSightAccessService } from 'Services/quicksight-access.service';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-graph',
  templateUrl: './graph.component.html',
  styleUrls: ['./graph.component.sass'],
})
export class GraphComponent {
  readonly embedUrl$ = this.quickSightAccessService.getEmbedUrl();

  readonly safeEmbedUrl$ = this.embedUrl$.pipe(map(({ embed_url }) => this.getSafeUrl(embed_url)));

  constructor(private quickSightAccessService: QuickSightAccessService, private sanitizer: DomSanitizer) {}

  getSafeUrl(url: string): SafeResourceUrl {
    return this.sanitizer.bypassSecurityTrustResourceUrl(url);
  }
}

```
### 埋め込みグラフ表示用のhtml
```html
<ng-container *ngIf="safeEmbedUrl$ | async as url">
  <iframe [src]="url" frameborder="0" height="1080px" width="100%"></iframe>
</ng-container>

```

## 結果
![スクリーンショット 2023-12-09 5.15.33（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264617/284e8aa7-1980-167a-3955-5fc2d682baa2.png)


これでQuickSightで作成したダッシュボードを埋め込むことができるようになりました！
ですがQuickSightの埋め込みではiframeを利用するため、そこだけ考慮しておいてください。

## 参考

[Amazon QuickSight を使用した開発 - Amazon QuickSight](https://docs.aws.amazon.com/ja_jp/quicksight/latest/user/quicksight_dev.html)
[GenerateEmbedUrlForRegisteredUserrRequest(AWS SDK for Rubyのドキュメント)](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/QuickSight/Types/GenerateEmbedUrlForRegisteredUserRequest.html)
[GenerateEmbedUrlForRegisteredUserResponse](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/QuickSight/Types/GenerateEmbedUrlForRegisteredUserResponse.html)
[登録済みユーザー向けの QuickSight データダッシュボードの埋め込み](https://docs.aws.amazon.com/ja_jp/quicksight/latest/user/embedded-analytics-dashboards-for-authenticated-users.html)