Google Drive: Search Folder

Google Drive: Search Folder

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

This item searches for a folder with a specific name, directly under the specified folder on Google Drive.

Auto Step icon
Basic Configs
Step Name
Note
Configs for this Auto Step
OAuth_V2
C1: Service Account Setting *
ParentFolderId
C2: Parent Folder ID *#{EL}
FolderName
C3: Folder Name to search for *#{EL}
FolderIdItem
C4: Data item that will save Folder ID
WebViewUrlItem
C5: Data item that will save web view URL of Folder

Notes

  • The folder ID is contained in the URL https://drive.google.com/drive/u/1/folders/(Folder ID)
  • If the folder can not be found, an error will occur
  • It is required that the parent folder be shared in advance with the service account set in [C1: Service Account Setting]
  • To set up [C1: Service Account Setting] :
    1. Prepare a service account on Google Cloud Console
    2. Create an OAuth2 JWT Bearer setting on Questetra BPM Suite and set it to C1
      • Scope https://www.googleapis.com/auth/drive.readonly is required
      • Set up other items as shown in the table below :
Settings on Questetra BPM SuiteCorresponding Information
of Google Cloud Service Account Key
Property Name in JSON FileRequired or Not
Client IDOAuth2 Client IDclient_idNot Required
Private Key IDKey IDprivate_key_idRequired
Private KeyPrivate Keyprivate_keyRequired
Custom Secret Information 1Email Addressclient_emailRequired

Capture

See also

Script (click to open)
  • An XML file that contains the code below is available to download
    • google-drive-folder-search.xml (C) Questetra, Inc. (MIT License)
    • If you are using Professional, you can modify the contents of this file and use it as your own add-on auto step

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);
    }
}

Scroll to Top

Discover more from Questetra Support

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

Continue reading