Start: Microsoft 365 OneDrive for Business: File Uploaded

Start: Microsoft 365 OneDrive for Business: File Uploaded

開始: Microsoft 365 OneDrive for Business: ファイルアップロード時

This item starts a process when a file has been uploaded on the specified OneDrive folder.

Basic Configs
Step Name
Note
Auto Step icon
Configs for this Auto Step
conf_OAuth2
C1: OAuth2 Setting *
conf_FolderUrl
C2: Folder URL to monitor (Root Folder if blank)
conf_urlData
C3: Data item to save file URL *
conf_timestampData
C4: Data item to save file uploaded datetime

Notes

  • This Start Event is for OneDrive for Business of Microsoft 365
    • It does not work for OneDrive personal
  • To get the URL of a file/folder on OneDrive, open the Details window of the file/folder by clicking the Information icon, proceed to “More details”, and click the icon next to “Path”

  • Questetra BPM Suite will periodically poll the OneDrive to check for new uploaded files
    • If any files have been uploaded, a process will be started
  • A Process will not be started on the first check; only the check will be done
    • You can confirm the check status from the “Process Log”
  • If a large number of files are uploaded in a short period of time, the processes may not be started for all files
    • Approx. 90 processes every 3 minutes ~ 15 minutes
  • If there are too many items (files / folders) in the folder (1000 as of December 2023), the check will be aborted
    • Control the number of items so that it does not increase too much

Capture

Script (click to open)
  • An XML file that contains the code below is available to download
    • onedrive-file-uploaded.xml (C) Questetra, Inc. (MIT License)
    • Just use it for a reference for the codes
    • This file cannot be imported into a Workflow App as an Add-on

// OAuth2 config sample at [OAuth 2.0 Setting]
// - Authorization Endpoint URL: https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/authorize
// - Token Endpoint URL: https://login.microsoftonline.com/{your-tenant-id}/oauth2/v2.0/token
// - Scope: https://graph.microsoft.com/Files.ReadWrite.All offline_access
// - Consumer Key: (Get by Microsoft Azure Active Directory)
// - Consumer Secret: (Get by Microsoft Azure Active Directory)

const DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssX";
const GRAPH_URI = 'https://graph.microsoft.com/v1.0/';
const LIMIT = 1000; // API の制限は明記されていない。1000 件以上のファイル/フォルダがある場合はエラーにする

/**
 * configs から必要な情報を取り出す
 * @returns {Object} setting 設定
 * @returns {string} setting.folderInfo 検索対象のフォルダの情報 {driveId, folderId}
 * @returns {AuthSettingWrapper} setting.oauth2 OAuth2 設定
 */
const prepare = () => {
    const oauth2 = configs.getObject('conf_OAuth2');
    let folderUrl = configs.get('conf_FolderUrl');
    const folderInfo = getFolderInfoByUrl(oauth2, folderUrl);

    return {
        oauth2,
        folderInfo
    };
};

/**
  * フォルダの URL からフォルダ情報(ドライブのパスとフォルダ ID)を取得し、
  * オブジェクトで返す(URL が空の場合はドライブのパスを me/drive、フォルダ ID を root にする)
  * @param {AuthSettingWrapper} oauth2  OAuth2 認証設定
  * @param {String} folderUrl  フォルダのURL
  * @return {Object} folderInfo  フォルダ情報
  * @return {String} folderInfo.drivePath  ドライブのパス
  * @return {String} folderInfo.folderId  フォルダ ID
  */
const getFolderInfoByUrl = (oauth2, folderUrl) => {
    if (folderUrl === '' || folderUrl === null) {
        return {
            drivePath: 'me/drive',
            folderId: 'root'
        };
    }
    const driveItem = getObjBySharingUrl(oauth2, folderUrl);
    if (driveItem.folder === undefined) {
        throw 'The specified URL is not a folder.';
    }
    return {
        drivePath: `drives/${driveItem.parentReference.driveId}`,
        folderId: driveItem.id
    };
};

/**
  * OneDriveのドライブアイテム(ファイル、フォルダ)のメタデータを取得し、JSONオブジェクトを返す
  * APIの仕様: https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/api/shares_get?view=odsp-graph-online
  * @param {AuthSettingWrapper} oauth2  OAuth2 認証設定
  * @param {String} sharingUrl  ドライブアイテムの共有 URL
  * @return {Object} responseObj  ドライブアイテムのメタデータのJSONオブジェクト
  */
const getObjBySharingUrl = (oauth2, sharingUrl) => {
    const encodedSharingUrl = encodeSharingUrl(sharingUrl);
    const response = httpClient.begin() // HttpRequestWrapper
        .authSetting(oauth2) // Request HEADER (OAuth2 Token)
        .queryParam('select', 'id,parentReference/driveId,folder')
        .get(`${GRAPH_URI}shares/${encodedSharingUrl}/driveItem`); // HttpResponseWrapper
    const httpStatus = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (httpStatus >= 300) {
        const accessLog = `---GET request--- ${httpStatus}\n${responseStr}\n`;
        engine.log(accessLog);
        throw `Failed to get drive item. status: ${httpStatus}`;
    }
    return JSON.parse(response.getResponseAsString());
};

/**
  * 共有URLをunpadded base64url 形式にエンコードする
  * @param {String} sharingUrl  共有URL
  * @return {String} encodedSharingUrl  エンコードされた共有URL
  */
const encodeSharingUrl = (sharingUrl) => {
    let encodedSharingUrl = base64.encodeToUrlSafeString(sharingUrl);
    while (encodedSharingUrl.slice(-1) === '=') {
        encodedSharingUrl = encodedSharingUrl.slice(0, -1);
    }
    return `u!${encodedSharingUrl}`;
};

/**
 * ファイルの検索
 * @param {number} limit ファイル数の上限
 * @param {timestamp} timestampLowerLimit timestamp の下限
 * @returns {Array} files ファイル一覧
 * @returns {string} files[].id ファイル ID
 * @returns {timestamp} files[].timestamp ファイルアップロード時刻
 */
const list = (limit, timestampLowerLimit) => {
    const {
        oauth2,
        folderInfo
    } = prepare();

    const driveItems = getChildren(oauth2, folderInfo);
    let files = driveItems
        .filter(driveItem => driveItem.file !== undefined && !engine.isProcessStarted(driveItem.id)) // ファイルのみに絞り込み、かつ未開始のファイル
        .map(formatFile) // 必要な情報のみ抜き出し、整形
        .filter(file => !file.timestamp.before(timestampLowerLimit)) // timestampLowerLimit 以降のデータのみに絞り込み
        .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); // 新しい順に並べ替え
    // 先頭から limit で切る
    files = files.slice(0, limit);
    logFiles(files);
    return files;
};

/**
 * 指定フォルダ内のドライブアイテムの一覧を取得する
 * フォルダ内のドライブアイテム (ファイル+フォルダ) 数が 1000 を超える場合、エラー
 * @param {AuthSettingWrapper} oauth2 OAuth2 設定
 * @param {Object} folderInfo 検索対象のフォルダ情報 {drivePath, folderId}
 * @returns {Array} driveItems ドライブアイテム一覧
 * @returns {string} driveItems[].id ドライブアイテム ID
 * @returns {timestamp} driveItems[].createdDateTime ドライブアイテム作成日時
 * @returns {Object} driveItems[].file ドライブアイテムのファイル情報(ファイルかどうかを判定するために使用)
 * @returns {string} driveItems[].webUrl ドライブアイテムの URL
 */
const getChildren = (oauth2, folderInfo) => {
    const url = `${GRAPH_URI}${folderInfo.drivePath}/items/${folderInfo.folderId}/children`;
    const response = httpClient.begin()
        .authSetting(oauth2)
        .queryParam('$top', `${LIMIT}`)
        .queryParam('$select', 'id,createdDateTime,file,webUrl')
        .get(url);
    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) {
        engine.log(responseStr);
        throw `Failed to get children of the folder. status: ${status}`;
    }
    const json = JSON.parse(responseStr);

    // 次のページを取得するリンクがある場合は、フォルダにまだファイル/フォルダがあるとみなして、エラーにする
    const nextLink = json['@odata.nextLink'];
    if (nextLink !== undefined) {
        throw `More than ${LIMIT} items are in the specified folder.`;
    }

    return json.value;
}

/**
 * OneDrive のファイルデータから、必要な部分のみ抜き出す
 * @param file ファイルデータ
 * @returns {Object} file ファイル
 * @returns {string} file.id ファイル ID
 * @returns {timestamp} file.timestamp ファイルアップロード時刻
 */
const formatFile = (file) => {
    const {
        id,
        createdDateTime,
        webUrl
    } = file;
    return {
        id,
        timestamp: dateFormatter.parse(DATETIME_FORMAT, createdDateTime),
        url: webUrl
    };
};

/**
 * ファイルのログ出力
 * @param {Array} files ファイル一覧
 */
const logFiles = (files) => {
    if (files.length === 0) {
        engine.log('no files');
        return;
    }
    const replacer = (key, value) => value instanceof java.sql.Timestamp ? dateFormatter.format(DATETIME_FORMAT, value) : value;
    files.forEach(file => engine.log(JSON.stringify(file, replacer)));
};

Scroll to Top

Discover more from Questetra Support

Subscribe now to keep reading and get access to the full archive.

Continue reading