Google ドライブ: フォルダ検索

Google ドライブ: フォルダ検索

Google Drive: Search Folder

この工程は、Google ドライブ の指定フォルダ直下に、特定の名前のフォルダがあるかどうか調べます。

Auto Step icon
Basic Configs
工程名
メモ
Configs for this Auto Step
OAuth_V2
C1: サービスアカウント設定 *
ParentFolderId
C2: 検索するフォルダの親フォルダの ID *#{EL}
FolderName
C3: 検索するフォルダの名称 *#{EL}
FolderIdItem
C4: 検索したフォルダの ID を保存するデータ項目
WebViewUrlItem
C5: 検索したフォルダの表示 URL を保存するデータ項目

Notes

  • フォルダの ID は、URL に含まれています https://drive.google.com/drive/u/1/folders/(Folder ID)
  • フォルダが見つからない場合、エラーになります
  • 親フォルダを、事前に[C1: サービスアカウント設定]のサービスアカウントと共有しておく必要があります
  • [C1: サービスアカウント設定]を設定するには:
    1. Google Cloud コンソールでサービスアカウントを準備します
    2. Questetra BPM Suite で OAuth2 JWT ベアラーフローの設定を作成し、C1 に設定します
      • スコープ https://www.googleapis.com/auth/drive.readonly が必要です
      • 以降の項目は、下表のとおり設定してください
Questetra BPM Suite の設定項目対応する Google Cloud
サービスアカウントキーの情報
JSON ファイルの項目名設定必須かどうか
クライアント IDOAuth2 クライアント IDclient_id任意
秘密鍵 IDキー IDprivate_key_id必須
秘密鍵秘密鍵private_key必須
カスタム秘密情報 1メールアドレスclient_email必須

Capture

See also

Script (click to open)
  • 次のスクリプトが記述されている XML ファイルをダウンロードできます
    • google-drive-folder-search.xml (C) Questetra, Inc. (MIT License)
    • Professional のワークフロー基盤では、ファイル内容を改変しオリジナルのアドオン自動工程として活用できます

function main() {
    //// == 工程コンフィグ・ワークフローデータの参照 / Config & Data Retrieving ==
    const oauthV2 = configs.getObject("OAuth_V2");
    let parentFolderId = configs.get("ParentFolderId");
    if (parentFolderId === "") {
        throw new Error("Parent Folder ID is blank.");
    }
    const folderName = configs.get("FolderName");
    if (folderName === "" || folderName === null) {
        throw new Error("Folder Name is blank.");
    }
    const idDataDef = configs.getObject("FolderIdItem");
    const urlDataDef = configs.getObject("WebViewUrlItem");
    // If neither C4 nor C5 are set, throw error
    if (idDataDef === null && urlDataDef === null) {
        throw new Error("Neither of Data Items to save result are set.");
    }

    //// == 演算 / Calculating ==
    const driveId = getDriveId(oauthV2, parentFolderId);
    const folders = searchFolder(oauthV2, driveId, parentFolderId, folderName);
    const folderNum = folders.length;
    if (folderNum === 0) {
        throw new Error(`Could not find Folder:${folderName} with Parent Folder ID:${parentFolderId}`);
    }
    const folderIdList = [];
    const folderUrlList = [];
    for (let i = 0; i < folderNum; i++) {
        folderIdList.push(folders[i].id);
        folderUrlList.push(folders[i].webViewLink);
    }

    //// == ワークフローデータへの代入 / Data Updating ==
    setFolderData(idDataDef, folderIdList);
    setFolderData(urlDataDef, folderUrlList);
}

const URL_TOKEN_REQUEST = 'https://oauth2.googleapis.com/token';
const SCOPE = 'https://www.googleapis.com/auth/drive.readonly';

/**
 * @param auth HTTP 認証設定
 * @returns {any} アクセストークンを含むオブジェクト
 */
const getAccessToken = (auth) => {
    const privateKeyId = auth.getPrivateKeyId();
    const privateKey = auth.getPrivateKey();
    const serviceAccount = auth.getCustomSecret1();
    const scope = auth.getScope();
    if (!scope.split(' ').includes(SCOPE)) {
        throw new Error(`Scope ${SCOPE} must be included in the scope.`);
    }
    if (privateKeyId === '') {
        throw new Error('Private Key ID is required.');
    }
    if (privateKey === '') {
        throw new Error('Private Key is required.');
    }
    if (serviceAccount === '') {
        throw new Error('Service Account must be set to Custom Secret 1.');
    }
    const header = {
        "alg": "RS256",
        "typ": "at+jwt",
        "kid": privateKeyId
    };
    const now = Math.floor(Date.now() / 1000);
    const payload = {
        "iss": serviceAccount,
        "aud": URL_TOKEN_REQUEST,
        "sub": '',
        "iat": now,
        "exp": now + 3600,
        /**
         * https://developers.google.com/identity/protocols/oauth2/service-account#jwt-auth
         * "without OAuth" の話だが、OAuth でも 1 hour になるようだ。
         * 1 hour より長ければエラー。短ければ、1 hour のトークンが返ってくる。
         */
        scope
    };
    const keyB = rsa.readKeyFromPkcs8(privateKey);
    const assertion = jwt.build(header, payload, keyB);

    const response = httpClient.begin()
        .formParam("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
        .formParam('assertion', assertion)
        .post(URL_TOKEN_REQUEST);
    const responseText = response.getResponseAsString();
    if (response.getStatusCode() !== 200) {
        engine.log(responseText);
        throw new Error(`Failed to get Access token. status: ${response.getStatusCode()}`);
    }
    const result = JSON.parse(response.getResponseAsString());
    if (result.access_token === undefined) {
        engine.log(responseText);
        throw new Error(`Failed to get Access token. access token not found.`);
    }
    return result;
};

/**
 * 親フォルダのドライブ ID を取得する
 * @param {AuthSettingWrapper} oauthV2  OAuth2 認証設定(サービスアカウント設定)
 * @param {String} parentFolderId 親フォルダの ID
 * @return {String} driveId ドライブ ID (共有ドライブになければ null)
 */
function getDriveId(oauthV2, parentFolderId) {
    if (parentFolderId === "root") {
        return null;
    }
    const url = `https://www.googleapis.com/drive/v3/files/${parentFolderId}`;
    let request = httpClient.begin();
    request = request.oauth2JwtBearer(oauthV2, () => getAccessToken(oauthV2));

    const response = request.queryParam("fields", "driveId")
        .queryParam("supportsAllDrives", "true")
        .get(url);
    const status = response.getStatusCode();
    const responseTxt = response.getResponseAsString();
    if (status >= 300) {
        const errorMsg = `Failed to get parent folder with parent folder ID:${parentFolderId}. status:${status}`;
        engine.log(responseTxt);
        throw new Error(errorMsg);
    }
    const driveId = JSON.parse(responseTxt).driveId;
    if (driveId === undefined) {
        return null;
    }
    return driveId;
}

/**
 * Google ドライブのフォルダを検索
 * @param {AuthSettingWrapper} oauthV2  OAuth2 認証設定(サービスアカウント設定)
 * @param {String} driveId 親フォルダのドライブ ID (共有ドライブになければ null)
 * @param {String} parentFolderId 親フォルダの ID
 * @param {String} folderName 検索するフォルダの名前
 * @return {Array} folders 検索結果一覧
 * @return {String} folders[].id フォルダの ID
 * @return {String} folders[].webViewLink フォルダの表示 URL
 */
function searchFolder(oauthV2, driveId, parentFolderId, folderName) {
    const folderNameRep = folderName.replace(/['\\]/g, "\\$&"); // ' と \ をエスケープ
    const q = `mimeType = 'application/vnd.google-apps.folder' and trashed = false and name = '${folderNameRep}' and '${parentFolderId}' in parents`;
    const url = "https://www.googleapis.com/drive/v3/files";
    let request = httpClient.begin();

    request = request.oauth2JwtBearer(oauthV2, () => getAccessToken(oauthV2));
    request = request.queryParam("q", q)
        .queryParam("pageSize", "1000")
        .queryParam("fields", "files(id,webViewLink)");
    if (driveId !== null) { // 親フォルダが共有ドライブにある場合
        request = request
            .queryParam("includeItemsFromAllDrives", "true")
            .queryParam("supportsAllDrives", "true")
            .queryParam("corpora", "drive")
            .queryParam("driveId", driveId);
    }
    const response = request.get(url);
    const status = response.getStatusCode();
    const responseTxt = response.getResponseAsString();
    if (status >= 300) {
        const errorMsg = `Failed to search. status:${status}`;
        engine.log(responseTxt);
        throw new Error(errorMsg);
    }
    const folders = JSON.parse(responseTxt).files;
    return folders;
}

/**
 * フォルダの情報をデータ項目にセットする
 * @param {ProcessDataDefinitionView} dataDef 保存先データ項目の ProcessDataDefinitionView
 * @param {Array<String>} folderInfoList 保存するフォルダ情報の配列
 */
function setFolderData(dataDef, folderInfoList) {
    if (dataDef !== null) {
        //Multiple Judge
        if (dataDef.matchDataType("STRING_TEXTFIELD") && folderInfoList.length > 1) {
            throw new Error("Multiple folders are found. Can't set data to single-line string Data Item.");
        }
        const folderInfoStr = folderInfoList.join("\n");
        engine.setData(dataDef, folderInfoStr);
    }
}

上部へスクロール

Questetra Supportをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む