Google Apps ScriptとLinear のデータを連携する方法
Google Apps Script を使えばGoogle スプレッドシート、Google ドキュメントといったGoogle のサービス内でカスタム機能を作成できます。CData Connect AI を使えば、Linear を含むCData がサポートする100以上のすべてのデータソースのSQL Server インターフェースを利用できます。SQL Server プロトコルはGoogle Apps Script のJDBC サービスでネイティブにサポートされているので、Connect AI を活用すればご利用のGoogle サービスからLinear のリアルタイムデータにアクセスできます。
この記事では、Connect AI でLinear に接続する方法を説明し、Google スプレッドシートでLinear のデータを扱うためのサンプルスクリプトを提供します。
Connect AI からLinear への接続
CData Connect AI では、直感的なクリック操作ベースのインターフェースを使ってデータソースに接続できます。
- Connect AI にログインし、 Add Connection をクリックします。
- Add Connection パネルから「Linear」を選択します。
-
必要な認証プロパティを入力し、Linear に接続します。
Linear への認証には、個人用 API キーまたは OAuth 2.0 を使用できます。ご自身の Linear アカウントで接続する場合は、API キーを使うのが最も手軽です。
API キーによる認証
以下の接続プロパティを設定します。
- AuthScheme:APIKey に設定します。
- APIKey:Linear の個人用 API キー。
個人用 API キーを作成するには、Linear にログインして Settings > Security & access > Personal API keys を開き、New API key を選択して作成します。キーは一度しか表示されないため、すぐにコピーしておきましょう。
OAuth による認証
OAuth を使用するには、Linear にカスタム OAuth アプリケーションを登録する必要があります(Settings > API > OAuth applications)。登録すると OAuthClientId と OAuthClientSecret が発行されます。対応しているフローは以下の 2 つです。
- Authorization code:AuthScheme を OAuth、InitiateOAuth を GETANDREFRESH に設定し、OAuthClientId、OAuthClientSecret、およびアプリケーションで定義した CallbackURL(例:http://localhost:33333)を指定します。ドライバーがブラウザで Linear を開くので、アクセスを許可してください。
- Client credentials:AuthScheme を OAuthClient に設定し、OAuthClientId と OAuthClientSecret を指定します。アプリケーション自体を認証するためブラウザ操作は不要で、マシン間(M2M)連携に適しています。
デフォルトでは、ドライバーは read,write スコープをリクエストします。アクセストークンは有効期限が切れると自動で更新されます。
- Create & Test をクリックします。
- 「Add Linear Connection」ページの「Permissions」タブに移動し、ユーザーベースのアクセス許可を更新します。
パーソナルアクセストークンの追加
OAuth 認証をサポートしていないサービス、アプリケーション、プラットフォーム、またはフレームワークから接続する場合は、認証に使用するパーソナルアクセストークン(PAT)を作成できます。きめ細かなアクセス管理を行うために、サービスごとに個別のPAT を作成するのがベストプラクティスです。
- Connect AI アプリの右上にあるユーザー名をクリックし、「Settings」をクリックします。
- 「Settings」ページで「Access Token」セクションにスクロールし、 Create PAT をクリックします。
- PAT の名前を入力して Create をクリックします。
- パーソナルアクセストークンは作成時にしか表示されないため、必ずコピーして安全に保存してください。
コネクションの設定が完了したら、Google Apps Script からLinear のデータへの接続準備ができました。
Apps Script からLinear のデータに接続
ここまでで、Connect AI でのLinear のコネクション設定は完了していると思います。ここからは、Google Apps Script からConnect AI に接続して、Google スプレッドシートでLinear のデータを扱います。
このセクションでは、スプレッドシートからLinear のデータを取得する、スクリプトを呼び出すメニューオプション付きのスクリプトを作成していきます。本記事用に、各部分にコメントをつけたサンプルスクリプトを作成しました。サンプルスクリプトは、記事の最後に記載しています。
1.空のスクリプトを作成
Google スプレッドシート用のスクリプトを作るには、Google スプレッドシートのメニューから拡張機能 Apps Script をクリックします。
2.クラス変数の宣言
スクリプトで作成した関数で利用するためのクラス変数をいくつか作成します。
//replace the variables in this block with real values as needed var address = 'tds.cdata.com:14333'; var user = 'CONNECT_USER'; // [email protected] var userPwd = 'CONNECT_USER_PAT'; var db = 'Linear1'; var dbUrl = 'jdbc:sqlserver://' + address + ';databaseName=' + db;
3.メニューオプションを追加
この関数はGoogle スプレッドシートに、作成した関数を呼び出すためのメニューオプションを追加します。
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{name:'Export data to a sheet', functionName: 'connectToLinearData'}
];
spreadsheet.addMenu('Linear のデータ', menuItems);
}
4.ヘルパー関数を作成
スプレッドシートの最初の空行を見つけるために、この関数を使用します。
/*
* Finds the first empty row in a spreadsheet by scanning an array of columns
* @return The row number of the first empty row.
*/
function getFirstEmptyRowByColumnArray(spreadSheet, column) {
var column = spreadSheet.getRange(column + ":" + column);
var values = column.getValues(); // get all data in one call
var ct = 0;
while ( values[ct] && values[ct][0] != "" ) {
ct++;
}
return (ct+1);
}
5.Linear のデータをスプレッドシートに書き込む関数を作成
以下の関数はGoogle Apps Script のJDBC 機能を使ってConnect AI に接続し、Linear のデータを書き込み、データをSELECT してスプレッドシートにデータを読み込みます。スクリプトを実行すると、2つの入力ボックスが表示されます。
最初のボックスはユーザーに、データを保持するためのシート名を入力するよう求めます。スプレッドシートが存在しなければ、関数側で作成します。
2つ目のボックスはユーザーに、読み込むLinear テーブル名を入力するよう求めます。無効なテーブルを選択すると、エラーメッセージが表示され関数は終了します。
関数はメニューオプションとして使用するよう設計されていますが、スプレッドシートの式として使用するよう拡張することもできます。
/*
* Reads data from a specified Linear 'table' and writes it to the specified sheet.
* (If the specified sheet does not exist, it is created.)
*/
function connectToLinearData() {
var thisWorkbook = SpreadsheetApp.getActive();
//select a sheet and create it if it does not exist
var selectedSheet = Browser.inputBox('Which sheet would you like the data to post to?',Browser.Buttons.OK_CANCEL);
if (selectedSheet == 'cancel')
return;
if (thisWorkbook.getSheetByName(selectedSheet) == null)
thisWorkbook.insertSheet(selectedSheet);
var resultSheet = thisWorkbook.getSheetByName(selectedSheet);
var rowNum = 2;
//select a Linear 'table'
var table = Browser.inputBox('Which table would you like to pull data from?',Browser.Buttons.OK_CANCEL);
if (table == 'cancel')
return;
var name = Jdbc.getConnection(dbUrl, {
user: user,
password: userPwd
}
);
//confirm that var table is a valid table/view
var dbMetaData = name.getMetaData();
var tableSet = dbMetaData.getTables(null, null, table, null);
var validTable = false;
while (tableSet.next()) {
var tempTable = tableSet.getString(3);
if (table.toUpperCase() == tempTable.toUpperCase()){
table = tempTable;
validTable = true;
break;
}
}
tableSet.close();
if (!validTable) {
Browser.msgBox("Invalid table name: " + table, Browser.Buttons.OK);
return;
}
var stmt = name.createStatement();
var results = stmt.executeQuery('SELECT * FROM ' + table);
var rsmd = results.getMetaData();
var numCols = rsmd.getColumnCount();
//if the sheet is empty, populate the first row with the headers
var firstEmptyRow = getFirstEmptyRowByColumnArray(resultSheet, "A");
if (firstEmptyRow == 1) {
//collect column names
var headers = new Array(new Array(numCols));
for (var col = 0; col < numCols; col++){
headers[0][col] = rsmd.getColumnName(col+1);
}
resultSheet.getRange(1, 1, headers.length, headers[0].length).setValues(headers);
} else {
rowNum = firstEmptyRow;
}
//write rows ofLinear のデータto the sheet
var values = new Array(new Array(numCols));
while (results.next()) {
for (var col = 0; col < numCols; col++) {
values[0][col] = results.getString(col + 1);
}
resultSheet.getRange(rowNum, 1, 1, numCols).setValues(values);
rowNum++;
}
results.close();
stmt.close();
}
関数の処理が完了すると、スプレッドシートにLinear のデータが読み込まれ、Google スプレッドシートの表計算、グラフ作成機能をどこでも自在に活用できます。
完全なGoogle Apps Script
//replace the variables in this block with real values as needed var address = 'tds.cdata.com:14333'; var user = 'CONNECT_USER'; // [email protected] var userPwd = 'CONNECT_USER_PAT'; var db = 'Linear1'; var dbUrl = 'jdbc:sqlserver://' + address + ';databaseName=' + db; function onOpen() { var spreadsheet = SpreadsheetApp.getActive(); var menuItems = [ {name:'Write table data to a sheet', functionName: 'connectToLinearData'} ]; spreadsheet.addMenu('Linear のデータ', menuItems); } /* * Finds the first empty row in a spreadsheet by scanning an array of columns * @return The row number of the first empty row. */ function getFirstEmptyRowByColumnArray(spreadSheet, column) { var column = spreadSheet.getRange(column + ":" + column); var values = column.getValues(); // get all data in one call var ct = 0; while ( values[ct] && values[ct][0] != "" ) { ct++; } return (ct+1); } /* * Reads data from a specified 'table' and writes it to the specified sheet. * (If the specified sheet does not exist, it is created.) */ function connectToLinearData() { var thisWorkbook = SpreadsheetApp.getActive(); //select a sheet and create it if it does not exist var selectedSheet = Browser.inputBox('Which sheet would you like the data to post to?',Browser.Buttons.OK_CANCEL); if (selectedSheet == 'cancel') return; if (thisWorkbook.getSheetByName(selectedSheet) == null) thisWorkbook.insertSheet(selectedSheet); var resultSheet = thisWorkbook.getSheetByName(selectedSheet); var rowNum = 2; //select a Linear 'table' var table = Browser.inputBox('Which table would you like to pull data from?',Browser.Buttons.OK_CANCEL); if (table == 'cancel') return; var name = Jdbc.getConnection(dbUrl, { user: user, password: userPwd } ); //confirm that var table is a valid table/view var dbMetaData = name.getMetaData(); var tableSet = dbMetaData.getTables(null, null, table, null); var validTable = false; while (tableSet.next()) { var tempTable = tableSet.getString(3); if (table.toUpperCase() == tempTable.toUpperCase()){ table = tempTable; validTable = true; break; } } tableSet.close(); if (!validTable) { Browser.msgBox("Invalid table name: " + table, Browser.Buttons.OK); return; } var stmt = name.createStatement(); var results = stmt.executeQuery('SELECT * FROM ' + table); var rsmd = results.getMetaData(); var numCols = rsmd.getColumnCount(); //if the sheet is empty, populate the first row with the headers var firstEmptyRow = getFirstEmptyRowByColumnArray(resultSheet, "A"); if (firstEmptyRow == 1) { //collect column names var headers = new Array(new Array(numCols)); for (var col = 0; col < numCols; col++){ headers[0][col] = rsmd.getColumnName(col+1); } resultSheet.getRange(1, 1, headers.length, headers[0].length).setValues(headers); } else { rowNum = firstEmptyRow; } //write rows ofLinear のデータto the sheet var values = new Array(new Array(numCols)); while (results.next()) { for (var col = 0; col < numCols; col++) { values[0][col] = results.getString(col + 1); } resultSheet.getRange(rowNum, 1, 1, numCols).setValues(values); rowNum++; } results.close(); stmt.close(); }