Box: Search File / Folder

Overview

Basic Configs
Step Name
Note
Auto Step icon
Configs for this Auto Step
conf_OAuth2
C1: OAuth2 Setting *
conf_ParentFolderId
C2: Parent Folder ID (Root Folder if blank)#{EL}
conf_Name
C3: Name to search for (case-insensitive) *#{EL}
conf_SearchFolder
C4: Search folders only, and abort if not found
conf_Status
C5: Data item to save search result status
conf_FoundId
C6: Data item to save ID of the found item
conf_FoundUrl
C7: Data item to save URL of the found item

Notes

  • Folder ID is contained in the URL: https://{sub-domain}.app.box.com/folder/(Folder ID)
  • Box refresh tokens have an expiration date
    • You must use them regularly to ensure that the expiration date is not exceeded (Box: Token & URL Expiration)

See Also

Script (click to open)
  • An XML file that contains the code below is available to download
    • box-file-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

const main = () => {
    //// == 工程コンフィグの参照 / Config Retrieving ==
    const oauth2 = configs.getObject('conf_OAuth2');
    const parentFolderId = retrieveParentFolderId();
    const name = configs.get('conf_Name');
    if (name === '' || name === null) {
        throw new Error('Name to search for is blank.');
    }
    const searchFolderOnly = configs.getObject('conf_SearchFolder');
    const statusDef = configs.getObject('conf_Status');
    const foundIdDef = configs.getObject('conf_FoundId');
    const foundUrlDef = configs.getObject('conf_FoundUrl');
    if (statusDef === null && foundIdDef === null && foundUrlDef === null) {
        throw new Error('None of the data items to save the result is set.');
    }

    //// == 演算 / Calculating ==
    const {foundId, foundUrl, status} = search(oauth2, parentFolderId, name, searchFolderOnly);

    //// == ワークフローデータへの代入 / Data Updating ==
    saveData(statusDef, status);
    saveData(foundIdDef, foundId);
    saveData(foundUrlDef, foundUrl);
};

/**
 * config から親フォルダの ID を読み出す(空の場合はルートフォルダの ID "0" を返す)
 * @returns {String}
 */
const retrieveParentFolderId = () => {
    const confName = 'conf_ParentFolderId';
    const parentFolderIdDef = configs.getObject(confName);
    let parentFolderId = configs.get(confName);
    if (parentFolderIdDef !== null) {
        parentFolderId = engine.findData(parentFolderIdDef);
    }
    if (parentFolderId === '' || parentFolderId === null) {
        parentFolderId = '0';
    }
    return parentFolderId;
};

const STATUS_FOUND_FOLDER = 'FOUND_FOLDER';
const STATUS_FOUND_FILE = 'FOUND_FILE';
const STATUS_FOUND_OTHER = 'FOUND_OTHER';
const STATUS_NOT_FOUND = 'NOT_FOUND';

/**
  * 検索する
  * 親フォルダ直下の子アイテムを "marker" が無くなるまで(または HTTP リクエスト数の上限に達するまで)ページングしながら走査し、
  * 名前が一致するアイテムを探す
  * @param {AuthSettingWrapper} oauth2 OAuth2 認証設定
  * @param {String} parentFolderId 親フォルダの ID
  * @param {String} name 検索する名前
  * @param {Boolean} searchFolderOnly フォルダのみを検索対象にし、見つからなかった場合は異常終了する
  * @return {Object} result 検索結果 {status, foundId, foundUrl}
  */
const search = (oauth2, parentFolderId, name, searchFolderOnly) => {
    const url = `https://api.box.com/2.0/folders/${parentFolderId}/items`;
    let marker = '';
    const limit = httpClient.getRequestingLimit();
    for (let count = 0; count < limit; count++) {
        const response = httpClient.begin()
            .authSetting(oauth2)
            .queryParam('fields', 'id,type,name')
            .queryParam('limit', '1000')
            .queryParam('usemarker', 'true')
            .queryParam('marker', marker)
            .get(url);
        const status = response.getStatusCode();
        const responseStr = response.getResponseAsString();
        if (status >= 300) {
            engine.log(responseStr);
            throw new Error(`Failed to search. status: ${status}`);
        }

        const json = JSON.parse(responseStr);
        const items = json.entries || [];
        const foundItem = items.find((item) => item.name.toLowerCase() === name.toLowerCase() && (!searchFolderOnly || item.type === 'folder'));
        if (foundItem !== undefined) {
            return {
                status: classify(foundItem),
                foundId: foundItem.id,
                foundUrl: buildUrl(foundItem)
            };
        }

        marker = json.next_marker;
        if (marker === undefined || marker === null || marker === '') {
            if (searchFolderOnly) {
                throw new Error(`Folder "${name}" was not found.`);
            }
            // 最終ページまで走査して見つからなかった場合は、正常な検索結果として NOT_FOUND を返す
            return { status: STATUS_NOT_FOUND, foundId: null, foundUrl: null };
        }
    }
    throw new Error('Too many files and folders are in the specified folder. The search could not be completed.');
};

/**
  * アイテムの type から検索結果ステータスを判定する
  * @param {Object} item 名前が一致したアイテム
  * @return {String} 検索結果ステータス
  */
const classify = (item) => {
    if (item.type === 'folder') {
        return STATUS_FOUND_FOLDER;
    }
    if (item.type === 'file') {
        return STATUS_FOUND_FILE;
    }
    return STATUS_FOUND_OTHER;
};

/**
  * アイテムの type と ID から Box の表示 URL を組み立てる
  * (file / folder 以外は表示 URL の形式が定まらないため null を返す)
  * @param {Object} item 名前が一致したアイテム
  * @return {String} 表示 URL(file / folder 以外は null)
  */
const buildUrl = (item) => {
    if (item.type === 'folder') {
        return `https://app.box.com/folder/${item.id}`;
    }
    if (item.type === 'file') {
        return `https://app.box.com/file/${item.id}`;
    }
    return null;
};

/**
 * データ項目への保存
 * @param {ProcessDataDefinitionView} def
 * @param {*} data
 */
const saveData = (def, data) => {
    if (def === null) {
        return;
    }
    engine.setData(def, data);
};

    

Discover more from Questetra Support

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

Continue reading