Microsoft Graph

[Google Apps Script]Microsoft Graph APIを使ってMicrosoftのサービスと連携する方法

Microsoft Graph APIを使って、OneDriveにあるExcelファイルからグラフを画像として取得し、Google スライドに挿入するスクリプトを書いてみました。

【エンジニア交流会】Google Apps Script 活用ミートアップ #3」のLT向けに考えた内容です。

Microsoft Graphとは?

当ブログでは、これまでMicrosoft Graph関連の様々な記事を書いてきました。

Microsoft Graph(以下Graph)とは、一言で言ってしまうと、“Office 365などのMicrosoftのクラウドサービス上で管理される様々なデータにアクセス可能なAPI”です。

Graphを使うことで、外部のアプリケーションやサービスから、ExchangeやSharePoint、OneDriveやExcelといった、製品をまたがった参照や操作を簡単に実行できるようになります。

しかもエンドポイントはただ一つ。
https://graph.microsoft.com』のみです。

公式サイトを見ると、具体的に何ができるのかが掴めると思います。

OAuth2 for Apps Scriptライブラリ

GraphはOAuth 2.0認証&RESTで呼び出せますが、Google Apps Scriptには外部APIを実行するのに非常に役立つライブラリ「OAuth2 for Apps Script」があります。

GoogleのエンジニアであるEric Koleda氏が作成した(らしい)ライブラリで、このライブラリが認証含めた面倒な処理を担ってくれるので、短いコードで簡潔に、APIを利用したコードを書くことができるようになります。

下準備

実際にコードを書く前に、上記ライブラリの設定やクライアントID、クライアントシークレットの取得といった、下準備をする必要があります。

  1. Google スライドを新規作成し、スクリプト エディタを開きます。
  2. リソースメニューから「ライブラリ」をクリックします。
  3. ライブラリを追加欄に下記スクリプトIDを入力して、「追加」ボタンをクリックします。
  4. 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF

  5. 「OAuth2」が追加されたらバージョンを選択(2018年10月時点では30が最新)し、識別子がデフォルトの「OAuth2」になっていることを確認後、「保存」ボタンをクリックします。
  6. ファイルメニューから「プロジェクトのプロパティ」をクリックします。
  7. プロジェクトのプロパティダイアログが表示されたら、「情報」タブから「スクリプト ID」をコピーして、メモ帳などのテキストエディタに貼り付けておきます。
  8. 手順6.で取得したスクリプトIDを含めた、下記URLがリダイレクトURLとなります。
  9. https://script.google.com/macros/d/(スクリプトID)/usercallback
  10. 下記記事を参考に、Application Registration Portalからアプリの登録を行います。アプリケーション ID(クライアントID)とアプリケーション シークレット(クライアントシークレット)は後で必要になるので、メモ帳などのテキストエディタにコピーしておきます。
    • プラットフォーム:Web
    • リダイレクト URL:上記リダイレクトURL
    • Microsoft Graph のアクセス許可:User.Read、Files.ReadWrite

以上で下準備作業は終了です。

OneDriveにあるExcelファイルからグラフを画像として取得するスクリプト

準備ができたので、下記コードを貼り付けて実行します。
クライアントIDとクライアントシークレット、OneDriveのルートフォルダーにあるExcelファイル名は適宜変更してください。

var CLIENT_ID = "(クライアントID)";
var CLIENT_SECRET = "(クライアントシークレット)";
var scopes = "User.Read Files.ReadWrite";
var endpointUrl = "https://graph.microsoft.com/v1.0"; //Microsoft Graphのエンドポイント
var fileName = "ChartSample.xlsx"; //OneDriveのルートフォルダーにあるExcelファイル名決め打ち

function onOpen() {
  var ui = SlidesApp.getUi();
  ui.createMenu("API Menu")
    .addItem("認証", "authorize")
    .addItem("ユーザー名取得", "getUserName")
    .addItem("Excel API呼び出し", "insertChartImage")
    .addItem("ログアウト", "reset")
    .addToUi();
}

//認証
function authorize() {
  var service = getService();
  if (!service.hasAccess()) {
    var authorizationUrl = service.getAuthorizationUrl();
    var template = HtmlService.createTemplate('<a href="<?= authorizationUrl ?>" target="_blank">認証ページを開きます</a>');
    template.authorizationUrl = authorizationUrl;
    var page = template.evaluate();
    SlidesApp.getUi().showModalDialog(page, "Authorize");
  } else {
    SlidesApp.getUi().alert("すでに認証済みです。");
  }
}

//OAuth2サービス設定
function getService() {
  return OAuth2.createService("Microsoft Graph")
    .setAuthorizationBaseUrl("https://login.microsoftonline.com/common/oauth2/v2.0/authorize")
    .setTokenUrl("https://login.microsoftonline.com/common/oauth2/v2.0/token")
    .setClientId(CLIENT_ID)
    .setClientSecret(CLIENT_SECRET)
    .setScope(scopes)
    .setCallbackFunction("authCallback")
    .setPropertyStore(PropertiesService.getUserProperties())
    .setParam("response_type", "code");
}

//認証コールバック
function authCallback(request) {
  var service = getService();
  var isAuthorized = service.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput("認証に成功しました。ページを閉じてください。");
  } else {
    return HtmlService.createHtmlOutput("認証に失敗しました。");
  }
}

//ログアウト
function reset() {
  getService().reset();
}

//ユーザー名取得
function getUserName() {
  var service = getService();
  if (service.hasAccess()) {
    var url = "https://graph.microsoft.com/v1.0/me";
    var response = UrlFetchApp.fetch(url, {
      headers: {
        Authorization: "Bearer " + service.getAccessToken()
      },
      method: "GET",
      contentType: "application/json"
    });
    var results = JSON.parse(response.getContentText());
    SlidesApp.getUi().alert("ユーザー名:" + results["displayName"]);
  } else {
    SlidesApp.getUi().alert("認証されていません。");
  }
}

//Excelファイルからグラフを取得→スライドに画像として挿入
function insertChartImage() {
  var slide = SlidesApp.getActivePresentation().getSlides()[0];
  var service = getService();
  if (service.hasAccess()) {
    //OneDriveのルートフォルダーにある対象ファイルのID取得
    var fileUrl = endpointUrl + "/me/drive/root/children/" + encodeURIComponent(fileName);
    var file = callGraphApi("GET", fileUrl);
    var fileId = file["id"];
    //ワークシートオブジェクトのコレクション取得
    var sheetsUrl = endpointUrl + "/me/drive/items/" + fileId + "/workbook/worksheets";
    var sheets = callGraphApi("GET", sheetsUrl);
    sheets["value"].forEach(function(sheet) {
      //グラフオブジェクトのコレクション取得
      var chartsUrl = sheetsUrl + "/" + sheet["name"] + "/" + "charts";
      var charts = callGraphApi("GET", chartsUrl);
      if (charts["value"].length > 0) {
        charts["value"].forEach(function(chart) {
          //グラフの画像を取得
          var imageUrl = chartsUrl + "('" + encodeURIComponent(chart["id"]) + "')/Image";
          var image = callGraphApi("GET", imageUrl);
          insertImageFromBase64(slide, image["value"]);
        });
      }
    });
  } else {
    SlidesApp.getUi().alert("認証されていません。");
  }
}

//Microsoft Graph APIの呼び出し
function callGraphApi(httpMethod, url) {
  var service = getService();
  if (service.hasAccess()) {
    var response = UrlFetchApp.fetch(url, {
      headers: {
        Authorization: "Bearer " + service.getAccessToken()
      },
      method: httpMethod,
      contentType: "application/json"
    });
    return JSON.parse(response.getContentText());
  }
}

//画像(Base64形式)の挿入
function insertImageFromBase64(slide, b64) {
  var blob = Utilities.newBlob(Utilities.base64Decode(b64), MimeType.PNG);
  slide.insertImage(blob);
}
  1. まずは、スクリプトの許可を行う為、「authorize」関数を実行します。
  2. 「承認が必要です」ダイアログが表示されたら、「許可を確認」ボタンをクリックします。
  3. アカウントの選択画面が表示されたら、使用するアカウントを選択します。
  4. 「このアプリは確認されていません」画面が表示されたら、詳細から「(安全ではないページ)に移動」をクリックします。
  5. アクセスのリクエスト画面が表示されたら、「許可」ボタンをクリックします。
  6. スライドを表示すると、「authorize」関数により作成されたダイアログが表示されていることが確認できるので、「認証ページを開きます」リンクをクリックします。
  7. 別タブでサインイン画面が表示されるので、Microsoft アカウントでサインインします。
  8. アプリの許可画面が表示されたら「はい」ボタンをクリックします。
  9. 「認証に成功しました。ページを閉じてください。」画面が表示されたら認証成功です。
  10. API Menu」(このメニューはonOpen関数を実行すると追加されます)から「ユーザー名取得」を実行すると、Microsoft アカウントのユーザー名が表示され、無事にGraph APIを呼び出せることが確認できます。
  11. API Menuから「Excel API呼び出し」を実行すると、OneDriveにあるExcelファイルからグラフを画像として取得し、スライドに挿入する処理が行われます。
  12. 無事に処理が行われることが確認できたら、最後はAPI Menuから「ログアウト」を実行して、サービスを切断します。

コードの解説

主要な部分だけ、簡単にコードの解説をしていきます。

//OAuth2サービス設定
function getService() {
  return OAuth2.createService("Microsoft Graph")
    .setAuthorizationBaseUrl("https://login.microsoftonline.com/common/oauth2/v2.0/authorize")
    .setTokenUrl("https://login.microsoftonline.com/common/oauth2/v2.0/token")
    .setClientId(CLIENT_ID)
    .setClientSecret(CLIENT_SECRET)
    .setScope(scopes)
    .setCallbackFunction("authCallback")
    .setPropertyStore(PropertiesService.getUserProperties())
    .setParam("response_type", "code");
}

スクリプトのメイン。
認証用のURLやトークン取得用のURL、クライアントIDやクライアントシークレットを設定することで、面倒くさい認証・トークン取得処理を行ってくれます(OAuth2 for Apps Scriptライブラリ)。

//認証
function authorize() {
  var service = getService();
  if (!service.hasAccess()) {
    var authorizationUrl = service.getAuthorizationUrl();
    var template = HtmlService.createTemplate('<a href="<?= authorizationUrl ?>" target="_blank">認証ページを開きます</a>');
    template.authorizationUrl = authorizationUrl;
    var page = template.evaluate();
    SlidesApp.getUi().showModalDialog(page, "Authorize");
  } else {
    SlidesApp.getUi().alert("すでに認証済みです。");
  }
}

トークンが取得できていない状態では、まず認証を行う必要があるため、認証用URLのリンクを表示させます。

//認証コールバック
function authCallback(request) {
  var service = getService();
  var isAuthorized = service.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput("認証に成功しました。ページを閉じてください。");
  } else {
    return HtmlService.createHtmlOutput("認証に失敗しました。");
  }
}

認証が終わったときに呼び出されるコールバックです。

//ユーザー名取得
function getUserName() {
  var service = getService();
  if (service.hasAccess()) {
    var url = "https://graph.microsoft.com/v1.0/me";
    var response = UrlFetchApp.fetch(url, {
      headers: {
        Authorization: "Bearer " + service.getAccessToken()
      },
      method: "GET",
      contentType: "application/json"
    });
    var results = JSON.parse(response.getContentText());
    SlidesApp.getUi().alert("ユーザー名:" + results["displayName"]);
  } else {
    SlidesApp.getUi().alert("認証されていません。");
  }
}

実際にGraph APIの呼び出しを行うコードです。
ユーザーの取得処理を行って、自分のユーザー名をメッセージボックスで表示します。

Excelファイルからグラフを画像として取得→スライドに挿入を行う「insertChartImage」関数では下記のような流れで処理を行っています。

  1. OneDriveのルートフォルダーにある対象ファイルのID取得
  2. https://graph.microsoft.com/v1.0/me/drive/root/children/{ファイル名}
  3. ワークシートオブジェクトのコレクション取得
  4. https://graph.microsoft.com/v1.0/me/drive/items/{ファイルID}/workbook/worksheets
  5. グラフオブジェクトのコレクション取得
  6. https://graph.microsoft.com/v1.0/me/drive/items/{ファイルID}/workbook/worksheets/{シート名}/charts
  7. グラフの画像をBase64形式で取得
  8. https://graph.microsoft.com/v1.0/me/drive/items/{ファイルID}/workbook/worksheets/{シート名}/charts/({グラフID})/Image
  9. Base64形式のデータからBlobオブジェクトを作成し、画像としてスライドに挿入
  10. //画像(Base64形式)の挿入
    function insertImageFromBase64(slide, b64) {
      var blob = Utilities.newBlob(Utilities.base64Decode(b64), MimeType.PNG);
      slide.insertImage(blob);
    }

おわりに

以上のように、Graph APIによってGASとMicrosoftのクラウドサービスとの連携がとれることが確認できました。

実は、3年ほど前にもGASとOutlookの連携について記事を書いていたのですが、そのときはOAuth2 for Apps Scriptライブラリの存在を知らなかったので、非常に冗長な処理になっています。

やはり、専用に作られたライブラリの力はスゴイですね!
OAuth周りの処理では、最早必須とも言えるライブラリです。

Graphは、今回取り上げたExcelとの連携以外にも、様々なサービスとの連携ができるようになっているので、GASからの利用に限らず、興味がある方は是非実装してみてください!

参考Webサイト

関連記事

  1. Google関連

    [Google Apps Script]サイドバーを表示する

    Google Apps Scriptでサイドバーを表示します。…

  2. Google関連

    【オトカドール】あそべるお店マップ「オトカマップ」を作ったよ。

    当ブログで何度も取り上げているオトカ&#9829;ドールですが、公式サ…

  3. Google関連

    「Save to Drive」ボタンを試してみました。

    下記Webページでも紹介されていますが、Webサイト上のファイルを直接…

  4. Office アドイン

    [Office用アプリ]Google ドライブでアプリを公開する方法

    今回は先日登壇した第一回 Apps for Office 勉強会の中で…

  5. Google関連

    【2018年9月版】Google API ConsoleでクライアントIDとクライアントシークレット…

    GoogleのAPIを呼び出すにあたり、クライアントIDとクライアント…

コメント

  • コメント (0)

  • トラックバックは利用できません。

  1. この記事へのコメントはありません。

Time limit is exhausted. Please reload CAPTCHA.

最近の記事

アーカイブ

RapidSSL_SEAL-90x50
PAGE TOP