Metabase x GAS 俺的メモ

2024/04/02に公開
//プロパティを利用する(旧エディタで手動入力)
const scriptProp = PropertiesService.getScriptProperties();
//metabase接続アカウント・URL情報
const BASE_URL = scriptProp.getProperty("BASE_URL");
const USER_NAME = scriptProp.getProperty("USER_NAME");
const PASSWORD = scriptProp.getProperty("PASSWORD");

function metabaseToSpreadsheet() {
  //取得したいIDと反映するシートをここで記述して渡す(ここは作る内容によって変更する)
  //欲しい情報が都度違うので現状手動で作っているが、スプレッドシートから入力できるようにしてもよさげ
  //https://metabase.xxx.xxx/question/XXX のXXXを入れる
  const questionList = [{
    id: 1,
    sheetName: "シート1",
    param: 1,
  },];

  //metabase認証
  const token = fetchToken();

  //metabaseからデータを取得してスプレッドシートに反映する
  questionList.map((question) => {
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(
      question.sheetName
    );
    //存在しない場合、シートを作成
    if (!sheet) createSheet(question.sheetName);
    //metabaseから情報を取得
    const response = fetchQuestion(token, question.id, question.param);
    //取得した情報をスプレッドシートに展開する
    const statusCode = response.getResponseCode();
    const result =
      statusCode == 200 || statusCode == 201 ?
        fillSheet(response, question.sheetName) : {
          success: false,
          error: "データの取得に失敗しました",
        };
    console.log(`
      id:${question.id}(${question.sheetName})
      success:${result.success}
      status:${statusCode}
      error:${result.error}
    `);
  });
}

//metabaseのセッション切れや初回接続時 tokenを返す
function fetchToken() {
  const sessionUrl = BASE_URL + "api/session";
  const options = {
    method: "post",
    headers: {
      "Content-Type": "application/json",
    },
    payload: JSON.stringify({
      username: USER_NAME,
      password: PASSWORD,
    }),
  };

  let response;
  try {
    response = UrlFetchApp.fetch(sessionUrl, options);
  } catch (e) {
    sendSlack(e)
    throw e;
  }
  const token = JSON.parse(response).id;
  return token;
}

//metabaseからデータを取得してレスポンスを返す
function fetchQuestion(token, id, param) {
  //※jsonで欲しい場合は/csvを/jsonにするとjson形式で取得可能
  questionUrl =
    BASE_URL + "api/card/" + id + "/query/csv?parameters=" + getParam(param);
  console.log("接続先:" + questionUrl);
  const options = {
    method: "post",
    headers: {
      "Content-Type": "application/json",
      "X-Metabase-Session": token,
    },
    muteHttpExceptions: true,
  };

  let response;
  try {
    response = UrlFetchApp.fetch(questionUrl, options);
  } catch (e) {
    return {
      success: false,
      error: e,
    };
  }
  return response;
}

//取得したmetabaseのデータを指定のシートに展開する
function fillSheet(response, name) {
  const values = Utilities.parseCsv(response.getContentText());
  try {
    sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name);
    const beforeMemberCount = sheet.getLastRow();
    sheet.clear({
      contentsOnly: true,
    });
    sheet.getRange(1, 1, values.length, values[0].length).setValues(values);
    const afterMemberCount = sheet.getLastRow();
    count = afterMemberCount - beforeMemberCount
    if (count !== 0) {
      let body = "";
      for (let i = 0; i < count; i++) {
        body = `${body}\n${values[values.length - 1 - i]}`;
      }
      sumsup = ":+1:"

      sendSlack(`${count}件 会員登録が増えました!${sumsup.repeat(count)} 【計${afterMemberCount - 1}件】\n${body}`);
    }
    return {
      success: true,
    };
  } catch (e) {
    console.log(e);
    return {
      success: false,
      error: e,
    };
  }
}

function getParam(param) {
  const template = [{
    target: ["variable", ["template-tag", "client_id"]],
    type: "category",
    value: param,
  },];
  return encodeURI(JSON.stringify(template));
}

//起動時にメニューを手動項目で追加する
function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu("Metabase")
    .addItem("手動更新", "metabaseToSpreadsheet")
    .addToUi();
  //必要があれば起動時に取得するようにも可
  //metabaseToSpreadsheet();
}

//シートが存在しない場合は作成する
function createSheet(name) {
  //コンテナバインドで紐付いたスプレッドシートを読み込み
  const mySheet = SpreadsheetApp.getActiveSpreadsheet();
  //スプレッドシートに新しいシートを追加挿入し、アクティブ化
  const newSheet = mySheet.insertSheet().activate();
  newSheet.setName(name);
  mySheet.moveActiveSheet(1);
}

function sendSlack(slackText) {
  var webHookUrl = "https://hooks.slack.com/services/xxxx/xxxx/xxxx";

  var jsonData = {
    text: "<!subteam^xxxxx>\n" + slackText,
    link_names: 1,
    username: "Bot",
  };
  var payload = JSON.stringify(jsonData);

  var options = {
    method: "post",
    contentType: "application/json",
    payload: payload,
    muteHttpExceptions: true,
  };

  UrlFetchApp.fetch(webHookUrl, options);
}

Discussion