TypescriptからMicrosoft Graph API使ってSharePointやOneDrive上のExcelの情報を読み込む

TypeScriptからSharePointやOneDriveのExcel Onlineの情報を読み込む方法を書きます。

読み込みたいファイル

項目
ディレクト OneDrive上の /otomo
ファイル名 sample.xlsx
シート名 SampleSheet
Cell B4:C4

このファイルの、

ここ読み込みます。

認証

Graph API使うための準備をします。

App Registration Portalへアプリケーション登録

まずはOAuth2で認可設定するために、こちらにアプリケーション登録します。

App Registration Portal

https://apps.dev.microsoft.com/

名前は適当に、

Generate New Password で生成したパスは client_secret として後で利用します。 Application Id は後で client_id として利用します。どちらもメモしておきます。

プラットフォームはWEBを作成します。手順の関係上、 http://localhost:3000/token をredirect_uriとして設定してください。

scopeはrefresh token使える offline_access と、自身のアクセス可能なファイルにアクセス許可する Files.Read.All を設定しました。

Tokenの取得

本当はアプリ内でハンドリングするのですが、今回はAccess TokenとRefresh Tokenを直接取得します。

こちらのサンプルアプリを利用します。

github.com

リポジトリをクローンして、パッケージをインストール。

$ git clone https://github.com/microsoftgraph/nodejs-connect-rest-sample.git
$ cd nodejs-connect-rest-sample/
$ npm i

先程の手順でメモした内容、設定した内容を元に少し修正します。

$ git diff -U2 app.js utils/config.js
diff --git a/app.js b/app.js
index 52c00e8..e111261 100644
--- a/app.js
+++ b/app.js
@@ -32,4 +32,8 @@ const app = express();
 // authentication setup
 const callback = (iss, sub, profile, accessToken, refreshToken, done) => {
+  console.log("AccessToken:")
+  console.log(accessToken)
+  console.log("RefreshToken:")
+  console.log(refreshToken)
   done(null, {
     profile,
diff --git a/utils/config.js b/utils/config.js
index ce62408..0465367 100644
--- a/utils/config.js
+++ b/utils/config.js
@@ -7,6 +7,6 @@ module.exports = {
   creds: {
     redirectUrl: 'http://localhost:3000/token',
-    clientID: 'ENTER_YOUR_CLIENT_ID',
-    clientSecret: 'ENTER_YOUR_SECRET',
+    clientID: 'yyyyyyyyyyyyyyyyyyyyyyyyyyyy',
+    clientSecret: 'xxxxxxxxxxxxxxxxxxxxx',
     identityMetadata: 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',
     allowHttpForRedirectUrl: true, // For development only
@@ -14,5 +14,5 @@ module.exports = {
     validateIssuer: false, // For development only
     responseMode: 'query',
-    scope: ['User.Read', 'Mail.Send', 'Files.ReadWrite']
+    scope: ['Files.Read.All', 'offline_access']
   }
 };

これでアプリケーションを起動します。

$ npm start

ブラウザから http://localhost:3000 にアクセスすると、以下のような画面になるので、 Connect to Microsoft Graph をクリックします。先程設定したscopeの許可が求められるので確認してOKします。

f:id:yomon8:20181107001554p:plain

コンソールに以下のようにAccess TokenとRefresh Tokenが表示されるはずです。

GET /stylesheets/style.css 304 2.360 ms - -
GET /login 302 306.523 ms - 0
AccessToken:
eyJ0eXAiOiJK...(省略
RefreshToken:
OAQABAAAAAA..(省略

TypeScriptからExcelへアクセスしてみる

config.json

設定したり、取得したりした情報をconfig.jsとして用意します。

{
    "grant_type": "refresh_token",
    "client_id": "yyyyyyyyyyyyyyyyyyyyyyyyyyyy",
    "client_secret": "xxxxxxxxxxxxxxxxxxxxx",
    "scope": "Files.Read.All offline_access",
    "redirect_uri": "http://localhost:3000/token",
    "refresh_token": "先程取得したRefreshToken"
}

index.ts

以下のようなスクリプトを用意しました。

Refresh Tokenを利用したAccess Tokenの更新はExpireの情報などを元にハンドルする必要がありますが、今回はシンプルにするために毎回Access Tokenを更新しています。

import * as MicrosoftGraph from "@microsoft/microsoft-graph-client"
import Axios, { AxiosRequestConfig } from "axios"
const config = require("./config.json")

// 取得したいEXCELの情報
const fileDir = '/otomo'
const fileName = 'sample.xlsx'
const sheetName = 'SampleSheet'
const cellRange = 'B4:C4'

async function getCell(dir: string, filename: string, sheetName: string, cellRange: string, config: AxiosRequestConfig) {
    let resToken = await Axios(config)
    const client = MicrosoftGraph.Client.init({
        authProvider: (done) => {
            done(null, resToken.data.access_token)
        }
    })

    // 参考:サポートされているクエリ、フィルター、およびページング オプション
    // https://msdn.microsoft.com/ja-jp/library/azure/ad/graph/howto/azure-ad-graph-api-supported-queries-filters-and-paging-options
    let resFile = await client.api(`/me/drive/root:${dir}:/children`).filter(`name eq '${filename}'`).get()

    // 参考: Microsoft Graph での Excel の操作
    // https://developer.microsoft.com/ja-jp/graph/docs/api-reference/v1.0/resources/excel
    let resCells = await client.api(`/me/drive/items/${resFile.value[0].id}/workbook/worksheets('${sheetName}')/range(address='${cellRange}')`).get()

    console.log(resCells)
}

const payload = Object.keys(config).map(key => key + "=" + encodeURIComponent(config[key])).join("&")
let requestConfig: AxiosRequestConfig = {
    url: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
    method: 'POST',
    headers: {
        'Content-Type': "application/x-www-form-urlencoded",
    },
    data: payload
}

getCell(fileDir, fileName, sheetName, cellRange, requestConfig)

利用しているパッケージはこちらです。

{
  "dependencies": {
    "@microsoft/microsoft-graph-client": "^1.3.0",
    "axios": "^0.18.0"
  },
  "devDependencies": {
    "@microsoft/microsoft-graph-types": "^1.5.0",
    "@types/node": "^10.12.2"
  }
}

実行してみる

実行してみると、ちゃんとCellの情報が取得できているのがわかります。

# コンパイル
$ tsc

# 実行
$ node index.js
{ '@odata.context': 'https://graph.microsoft.com/v1.0/$metadata#workbookRange',
  '@odata.type': '#microsoft.graph.workbookRange',
  '@odata.id':
   "/users('xxxxxxxx')/drive/items('xxxxxxxxxxx')/workbook/worksheets(%27%7B00000000-0001-0000-0000-000000000000%7D%27)/range(address=%27B4:C4%27)",
  address: 'SampleSheet!B4:C4',
  addressLocal: 'SampleSheet!B4:C4',
  cellCount: 2,
  columnCount: 2,
  columnHidden: false,
  columnIndex: 1,
  formulas: [ [ 43410, 'テスト3' ] ],
  formulasLocal: [ [ 43410, 'テスト3' ] ],
  formulasR1C1: [ [ 43410, 'テスト3' ] ],
  hidden: false,
  numberFormat: [ [ 'm/d/yyyy', 'General' ] ],
  rowCount: 1,
  rowHidden: false,
  rowIndex: 3,
  text: [ [ '2018/11/6', 'テスト3' ] ],
  values: [ [ 43410, 'テスト3' ] ],
  valueTypes: [ [ 'Double', 'String' ] ] }

参考

Graph エクスプローラー - Microsoft Graph

GitHub - microsoftgraph/msgraph-sdk-javascript: Microsoft Graph client library for JavaScript

Microsoft GraphでOAuthのアクセストークンを更新する仕組みを作る - Qiita