AngularJS を使用し、Presto のデータで動的なWeb ページを構築

古川えりか
古川えりか
コンテンツスペシャリスト
Presto に接続できるシングルページアプリケーションを作成します。



AngularJS (Angular) は、動的なWeb アプリの構造フレームワークです。CData API Server であるAngular およびADO.NET Provider for Presto (または250+ その他のADO.NET Providers) のCData API Server を使用して、Presto からリアルタイムデータにアクセスできるシングルページアプリケーション(SPAs) を構築できます。 この記事では、CData API Server の設定と、Presto へのライブアクセスを持つ単純なSPA の作成について説明します。 SPA はHTML テーブルを動的に作成して入力します。

Presto データ連携について

CData を使用すれば、Trino および Presto SQL エンジンのライブデータへのアクセスと統合がこれまでになく簡単になります。お客様は CData の接続機能を以下の目的で利用しています:

  • Trino v345 以降(旧 PrestoSQL)および Presto v0.242 以降(旧 PrestoDB)のデータにアクセスできます。
  • Trino または Presto インスタンスの基盤となるすべてのデータに対して読み取り・書き込みアクセスができます。
  • 最大スループットのための最適化されたクエリ生成。

Presto と Trino により、ユーザーは単一のエンドポイントを通じてさまざまな基盤データソースにアクセスできます。CData の接続と組み合わせることで、ユーザーはインスタンスへの純粋な SQL-92 アクセスを取得し、ビジネスデータをデータウェアハウスに統合したり、Power BI や Tableau などのお気に入りのツールからライブデータに直接簡単にアクセスしたりできます。

多くの場合、CData のライブ接続は、ツールで利用可能なネイティブのインポート機能を上回ります。あるお客様は、レポートに必要なデータセットのサイズが大きいため、Power BI を効果的に使用できませんでした。同社が CData Power BI Connector for Presto を導入したところ、DirectQuery 接続モードを使用してリアルタイムでレポートを生成できるようになりました。


はじめに


API Server の設定

以下のリンクからAPI Server の無償トライアルをスタートしたら、セキュアなPresto OData サービスを作成していきましょう。

Presto への接続

Angular からPresto のデータを操作するには、まずPresto への接続を作成・設定します。

  1. API Server にログインして、「Connections」をクリック、さらに「接続を追加」をクリックします。 接続を追加
  2. 「接続を追加」をクリックして、データソースがAPI Server に事前にインストールされている場合は、一覧から「Presto」を選択します。
  3. 事前にインストールされていない場合は、コネクタを追加していきます。コネクタ追加の手順は以下の記事にまとめてありますので、ご確認ください。
    CData コネクタの追加方法はこちら >>
  4. それでは、Presto への接続設定を行っていきましょう! 接続設定
  5. Presto への接続には、まずはServer およびPort を接続プロパティとして設定します。それ以外の追加項目は接続方式によって異なります。

    TLS/SSL を有効化するには、UseSSL をTRUE に設定します。

    LDAP で認証

    LDAP で認証するには、次の接続プロパティを設定します:

    • AuthScheme: LDAP に設定。
    • User: LDAP で接続するユーザー名。
    • Password: LDAP で接続するユーザーのパスワード。

    Kerberos 認証

    KERBEROS 認証を使う場合には、以下を設定します:

    • AuthScheme: KERBEROS に設定。
    • KerberosKDC: 接続するユーザーのKerberos Key Distribution Center (KDC) サービス。
    • KerberosRealm: 接続するユーザーのKerberos Realm 。
    • KerberosSPN: Kerberos Domain Controller のService Principal Name。
    • KerberosKeytabFile: Kerberos principals とencrypted keys を含むKeytab file。
    • User: Kerberos のユーザー。
    • Password: Kerberos で認証するユーザーのパスワード。
  6. 接続情報の入力が完了したら、「保存およびテスト」をクリックします。

Presto への接続には、まずはServer およびPort を接続プロパティとして設定します。それ以外の追加項目は接続方式によって異なります。

TLS/SSL を有効化するには、UseSSL をTRUE に設定します。

LDAP で認証

LDAP で認証するには、次の接続プロパティを設定します:

  • AuthScheme: LDAP に設定。
  • User: LDAP で接続するユーザー名。
  • Password: LDAP で接続するユーザーのパスワード。

Kerberos 認証

KERBEROS 認証を使う場合には、以下を設定します:

  • AuthScheme: KERBEROS に設定。
  • KerberosKDC: 接続するユーザーのKerberos Key Distribution Center (KDC) サービス。
  • KerberosRealm: 接続するユーザーのKerberos Realm 。
  • KerberosSPN: Kerberos Domain Controller のService Principal Name。
  • KerberosKeytabFile: Kerberos principals とencrypted keys を含むKeytab file。
  • User: Kerberos のユーザー。
  • Password: Kerberos で認証するユーザーのパスワード。

API Server のユーザー設定

次に、API Server 経由でPresto にアクセスするユーザーを作成します。「Users」ページでユーザーを追加・設定できます。やってみましょう。

  1. 「Users」ページで ユーザーを追加をクリックすると、「ユーザーを追加」ポップアップが開きます。
  2. 次に、「ロール」、「ユーザー名」、「権限」プロパティを設定し、「ユーザーを追加」をクリックします。
  3. その後、ユーザーの認証トークンが生成されます。各ユーザーの認証トークンとその他の情報は「Users」ページで確認できます。

Presto 用のAPI エンドポイントの作成

ユーザーを作成したら、Presto のデータ用のAPI エンドポイントを作成していきます。

  1. まず、「API」ページに移動し、 「 テーブルを追加」をクリックします。
  2. アクセスしたい接続を選択し、次へをクリックします。
  3. 接続を選択した状態で、各テーブルを選択して確認をクリックすることでエンドポイントを作成します。

OData のエンドポイントを取得

以上でPresto への接続を設定してユーザーを作成し、API Server でPresto データのAPI を追加しました。これで、OData 形式のPresto データをREST API で利用できます。API Server の「API」ページから、API のエンドポイントを表示およびコピーできます。

CORS を有効にする

AngularJS では、サーバーでCORS (Cross-origin resource sharing) を有効にする必要があります。API Server の[SETTINGS Server]タブに移動すると、CORS を有効にできます。 以下の設定を調整する必要があります。

  • チェックボックスをクリックして「Enable cross-origin resource sharing (CORS)]をクリックします。
  • チェックボックスをクリックして[Allow all domains without '*']を選択するか、Access-Control-Allow-Origin で接続を許可するドメインを指定します。
  • Access-Control-Allow-Methods を[GET,PUT,POST,OPTIONS]に設定します。
  • Access-Control-Allow-Headers を[authorization]に設定します。
  • [Save Changes]をクリックします。

OData フィードのサンプルURL

Presto への接続を構成し、ユーザーを作成してAPI Server にテーブルを追加すれば、それらのテーブルのOData フィードにアクセスできます。 以下は、テーブルにアクセスするためのURL とテーブルのリストです。テーブルへのアクセスについては、([API Server Web]ページの右上にあるAPI リンクをクリックして)API Server のAPI ページに移動できます。 URL には、API Server のアドレスポートが必要です。 Angular を使用しているため、デフォルトではJSON データを返さないURL の末尾に@json パラメータを追加します。

Table         URL
Entity (table) List http://address:port/api.rsc/
Metadata for table Customer http://address:port/api.rsc/Customer/$metadata?@json
Account http://address:port/api.rsc/Customer

返されるフィールドを制限する場合には、標準のOData フィードと同様に、$select パラメータを$filter、$orderby、$skip、$top などの他の標準のOData URL パラメータとともにクエリに追加できます。 サポートされているOData クエリの詳細については、ヘルプドキュメントを参照してください。

シングルページアプリケーションの構築

API Server のセットアップが完了したら、SPA を構築する準備が整います。これは単純なデモンストレーションなので、すべてのCSS、スクリプト、およびAngular コントローラーを1 つのファイルに含め、AngularJS サービス、ファクトリ、およびカスタムディレクティブによって提供される機能を意図的に使用しないようにします。

CSS 定義とAngularJS ライブラリのインポート

まずCSS ルールセットをいくつか作成し、table、th、td、tr 要素を変更してデータのテーブルをフォーマットします。また、SPA で使用するためにAngularJS ライブラリをインポートする必要があります。


  <style>
  table, th, td {
    border:1px solid grey;
    border-collapse: collapse;
    padding:5px;
  }
  table tr:nth-child(odd) {
    background-color: #f1f1f1;
  }
  table tr:nth-child(even) {
    background-color: #ffffff;
  }
  </style>
  <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>

Angular アプリとコントローラーオブジェクトの作成と参照

次に、HTML の[body]タグにng-app ディレクティブとng-controller ディレクティブを追加します。これは、本体がAngular を使用す唯一の場所であるために必要な操作です。 そして、HTML 本文の最後にAngular アプリとコントローラーを作成及び定義するスクリプトタグを作成します。


<body ng-app="DataApp" ng-controller="SimpleController">
...
<script>
var app = angular.module('DataApp', []);
app.controller('SimpleController', function($scope, $http) {
    //we will add code here
    });
</script>
</body>

コントローラーの定義

ここでのコントローラーはAngular オブジェクトを初期化してSPA を設定するinit、選択したテーブルのカラムを取得するgetTableColumns、選択したカラムから選択したフィールドのデータを取得するgetTableData の3つの関数で構成されます。 コントローラを作成するときの最初のアクションとして、init 関数を呼び出します。 他のすべての関数は必要に応じて呼び出され、これらの関数呼び出しでPresto のデータを取得するために、API Server に対して必要なHTTP GET 呼び出しを行います。


init();

/*
 * Initialize the data object, which will be used with Angular to
 * build the different parts of our SPA and to retrieve data from
 * the API Server.
 */
function init() {
  $scope.data = {
  availableTables: [],
                 availableColumns: [],
                 selectedTable: {},
                 tableData: []
  };

  /*
   * Call to the API Server to get the list of Tables, select the
   * first table by default, and retrieve the available columns.
   *
   * The call to the API Server returns standard OData, so the
   * data we need is in the value object in the JSON returned.
   */
  $http.get("http://server:port/api.rsc",{headers: {"Authorization":"Basic "  + btoa("MyUser:MyAuthtoken")}})
    .then(function (response) {
        $scope.data.availableTables = response.data.value;
        $scope.data.selectedTable = $scope.data.availableTables[0];
        $scope.getTableColumns();
        });
}

/*
 * Call to the API Server to get the list of columns for the
 * selected table.
 *
 * The data returned here is not standard OData, so we drill
 * down into the response to extract exactly the data we need
 * (an array of column names).
 *
 * With the column names retrieved, we will transform the array
 * of column names into an array of objects with a name and Id
 * field, to be used when we build an HTML select.
 */
$scope.getTableColumns = function () {
  $scope.data.tableData = [];
  $scope.data.selectedColumns = [];
  table = $scope.data.selectedTable.url;
  if (table != "") {
    $http.get("http://server:port/api.rsc/" + table + "/$metadata?@json", {headers: {"Authorization":"Basic "  + btoa("MyUser:MyAuthtoken")}})
      .then(function (response) {
          $scope.data.availableColumns = response.data.items[0]["odata:cname"];
          for (i = 0; i < $scope.data.availableColumns.length; i++) {
            $scope.data.availableColumns[i] = { id: i, name: $scope.data.availableColumns[i] };
          }
          });
  }
}

/*
 * Call to the API Server to get the requested data.We get the data
 * based on the table selected in the associated HTML select.
 * Then we create a comma-separated string of the selected columns.
 *
 * With the table and columns known, we can make the appropriate call
 * to the API Server.Because the driver returns standard OData, the
 * table data is found in the value field of the response.
 */
$scope.getTableData = function () {
  table = $scope.data.selectedTable.url;
  columnsArray = $scope.data.selectedColumns;
  columnString = "";
  for (i = 0; i < columnsArray.length; i++) {
    if (columnString != "") {
      columnString += ",";
    }
    columnString += columnsArray[i].name;
  }

  if (table != "") {
    $http.get("http://server:port/api.rsc/" + table + "?$select=" + columnString, {headers: {"Authorization":"Basic "  + btoa("MyUser:MyAuthtoken")}})
      .then(function (response) { $scope.data.tableData = response.data.value; });
  } else {
    $scope.data.tableData = [];
  }
}

Web ページの構成

コントローラを定義したら、Angular を使用してWeb ページを構築する準備が整います。単純なページには、テーブルを選択するための選択ボックス、カラムを選択するための選択(複数)ボックス、データを取得するためのボタン、およびデータを表示するためのテーブルの4つの主要部分があります。 これら4つの部分の1つずつを通して、Angular の使用方法について説明します。

テーブルを選択

最初のselect 要素では、ng-options ディレクティブを使用し、使用可能なテーブル(前述のinit 関数から取得) を反復処理し、select 要素にデータを入力します。 ng-model ディレクティブを使用して、選択したオプションの値をdata.selectedTable フィールドに割り当てます。 選択したテーブルが変更された場合は、getTableColumns 関数を呼び出して使用可能なカラムを再設定します。


  <label>Select a Table</label>
  <br />
  <select name="tableDropDown" id="tableDropDown"
          ng-options="table.name for table in data.availableTables track by table.url"
          ng-model="data.selectedTable"
          ng-change="getTableColumns()">
  </select>

カラムを選択

2番目のselect 要素では、再びng-options ディレクティブを使用しますが、今回は、(getTableColumns 関数によって取得された)使用可能なカラムを反復処理します。 使いやすさのために、select 要素に入力する前にカラムを名前でソートします。 この選択には複数 の属性が含まれているため、複数のカラムを選択できます。 選択した各列がdata.selectedColumns 配列に追加されます。カラムを選択すると、各列のテーブルヘッダーが作成されます(以下のデータテーブルのセクションを参照)。


  <label>Select Columns</label>
  <br />
  <select name="columnMultiple" id="columnMultiple"
          ng-options="column.name for column in data.availableColumns | orderBy:'name' track by column.id"
          ng-model="data.selectedColumns"
          multiple>
  </select>

テーブルデータを取得

このボタンでは、ボタンがクリックされるたびにgetTableData 関数を呼び出します。ユーザーがカラムを選択していない場合に、ng-disabled ディレクティブを使用してボタンを無効にしていることに注意してください。 また、選択したテーブルの名前でボタンのテキストを動的に更新します。


  <button name="getTableData" id="btnGetTableData"
          ng-click="getTableData()"
          ng-disabled="data.selectedColumns.length == 0">
  Get {{data.selectedTable.name}} Data
  </button>

テーブルデータを表示

このセクションは、選択したテーブルのデータを表示するというSPA の最終目標を満たしています。そのために、いくつかのng-repeat ディレクティブを使用します。1つは選択したカラムを反復処理してテーブルヘッダーを作成し、もう一つは返されたデータの行を反復処理して対応するデータを特定の行に表示します。

Angular を使用することで、表示するカラムを動的に決定できます。データがクリックされる「前」に選択されたカラムのみにデータが含まれることに注意してください。 使用可能なすべてのカラムを選択し、ボタンをクリックしてテーブルデータを取得してから、戻って別のカラムを選択または選択解除し、表示されるデータを変更することは簡単にできます。 選択したテーブルを変更すると、すべてのデータがクリアされます。


  <table>
    <tr>
      <th ng-repeat="column in data.selectedColumns | orderBy:'name'">{{column.name}}</th>
    </tr>
    <tr ng-repeat="row in data.tableData">
      <td ng-repeat="column in data.selectedColumns">{{ row[column.name] }}</td>
    </tr>
  </table>

アプリの完成

<!DOCTYPE html>

<html>
<style>
table, th, td {
border:1px solid grey;
        border-collapse: collapse;
padding:5px;
}
table tr:nth-child(odd) {
  background-color: #f1f1f1;
}
table tr:nth-child(even) {
  background-color: #ffffff;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body ng-app="DataApp" ng-controller="SimpleController">
<label>Select a Table</label>
<br>
<select name="tableDropDown" id="tableDropDown"
ng-options="table.name for table in data.availableTables track by table.url"
ng-model="data.selectedTable"
ng-change="getTableColumns()">
</select>
<label>Select Columns</label>
<br />
<select name="columnMultiple" id="columnMultiple"
ng-options="column.name for column in data.availableColumns | orderBy:'name' track by column.id"
ng-model="data.selectedColumns"
multiple>
</select>
<button name="getTableData" id="btnGetTableData"
ng-click="getTableData()"
ng-disabled="data.selectedColumns.length == 0">
Get {{data.selectedTable.name}} Data
</button>
<br />
<br />

<table>
<tr>
<th ng-repeat="column in data.selectedColumns | orderBy:'name'">{{column.name}}</th>
</tr>
<tr ng-repeat="row in data.tableData">
<td ng-repeat="column in data.selectedColumns">{{ row[column.name] }}</td>
</tr>
</table>
<script>
var app = angular.module('DataApp', []);
app.controller('SimpleController', function($scope, $http) {
    init();

    /*
     * Initialize the data object, which will be used with Angular to
     * build the different parts of our SPA and to retrieve data from
     * the API Server.
     */
    function init() {
    $scope.data = {
    availableTables: [],
    availableColumns: [],
    selectedTable: {},
    tableData: []
};

/*
 * Call to the API Server to get the list of tables, select the
 * first table by default, and retrieve the available columns.
 *
 * The call to the API Server returns standard OData, so the
 * data we need is in the value object in the JSON returned.
 */
$http.get("http://server:port/api.rsc",{headers: {"Authorization":"Basic "  + btoa("MyUser:MyAuthtoken")}})
.then(function (response) {
    $scope.data.availableTables = response.data.value;
    $scope.data.selectedTable = $scope.data.availableTables[0];
    $scope.getTableColumns();
    });
}

/*
 * Call to the API Server to get the list of columns for the
 * selected table.
 *
 * The data returned here is not standard OData, so we drill
 * down into the response to extract exactly the data we need
 * (an array of column names).
 *
 * With the column names retrieved, we will transform the array
 * of column names into an array of objects with a name and Id
 * field, to be used when we build an HTML select.
 */
$scope.getTableColumns = function () {
  $scope.data.tableData = [];
  $scope.data.selectedColumns = [];
  table = $scope.data.selectedTable.url;
  if (table != "") {
    $http.get("http://server:port/api.rsc/" + table + "/$metadata?@json", {headers: {"Authorization":"Basic "  + btoa("MyUser:MyAuthtoken")}})
      .then(function (response) {
          $scope.data.availableColumns = response.data.items[0]["odata:cname"];
          for (i = 0; i < $scope.data.availableColumns.length; i++) {
          $scope.data.availableColumns[i] = { id: i, name: $scope.data.availableColumns[i] };
          }
          });
  }
}

/*
 * Call to the API Server to get the requested data.We get the data
 * based on the table selected in the associated HTML select.
 * Then we create a comma-separated string of the selected columns.
 *
 * With the table and columns known, we can make the appropriate call
 * to the API Server.Because the driver returns standard OData, the
 * table data is found in the value field of the response.
 */
$scope.getTableData = function () {
  table = $scope.data.selectedTable.url;
  columnsArray = $scope.data.selectedColumns;
  columnString = "";
  for (i = 0; i < columnsArray.length; i++) {
    if (columnString != "") {
      columnString += ",";
    }
    columnString += columnsArray[i].name;
  }

  if (table != "") {
    $http.get("http://server:port/api.rsc/" + table + "?$select=" + columnString, {headers: {"Authorization":"Basic "  + btoa("MyUser:MyAuthtoken")}})
      .then(function (response) { $scope.data.tableData = response.data.value; });
  } else {
    $scope.data.tableData = [];
  }
}
});
</script>
</body>
</html>

無料トライアルと詳細

Angular で構築されたWeb アプリケーションからPresto (またはサポートされている他のデータソースからのデータ)に接続したい場合は、今すぐAPI Server の30日間無料トライアルダウンロードしてください。 API のより一般的な情報およびサポートされている他のデータソースについてはAPI Server ページを参照してください。

はじめる準備はできましたか?

詳細はこちら、または無料トライアルにお申し込みください:

CData API Server お問い合わせ