Box: Add Metadata to File
This item adds metadata to the specified file on Box, using a metadata template defined within your enterprise. An error occurs if the specified metadata template is already added to the file.
Configs: Common
  • Step Name
  • Note
Configs
  • C1: OAuth2 Setting *
  • C2: File ID to add metadata to *
  • C3: Display Name of Metadata Template *
  • C-K1: Display Name of Field 1
  • C-V1: Value for Field 1#{EL}
  • C-K2: Display Name of Field 2
  • C-V2: Value for Field 2#{EL}
  • C-K3: Display Name of Field 3
  • C-V3: Value for Field 3#{EL}
  • C-K4: Display Name of Field 4
  • C-V4: Value for Field 4#{EL}
  • C-K5: Display Name of Field 5
  • C-V5: Value for Field 5#{EL}
  • C-K6: Display Name of Field 6
  • C-V6: Value for Field 6#{EL}
  • C-K7: Display Name of Field 7
  • C-V7: Value for Field 7#{EL}
  • C-K8: Display Name of Field 8
  • C-V8: Value for Field 8#{EL}

Notes

  • The refresh token for Box has an expiration date. Use regularly to ensure that it does not exceed the expiration.
  • To create a metadata template on Box, you have to be a Box administrator. For more details, see the Box support page.
    • Please avoid duplication when naming metadata templates and fields as this item identifies them by their display names.
  • Hidden templates and hidden fields are not supported.
  • Values are to be set in string. The data format of the string is determined by the field type of the metadata.
    • Text: Any string
    • Number: Integer or decimal. Decimal point must be a period (.) and no other delimiters are allowed.
    • Date: yyyy-MM-dd
    • Dropdown – Single Select: An option value
    • Dropdown- Multiple Select: An option value (Multiple select is not supported)

Capture

See also

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


const FIELD_NUM = 8; // 扱えるフィールドの数

main();
function main(){
    const oauth2 = configs.get('conf_OAuth2');
    const fileId = decideFileId();
    const templateName = configs.get('conf_TemplateName');
    const metadataMap = retrieveMetadataMap();

    const {templateKey, fields} = searchTemplate(oauth2, templateName);
    const metadataObj = buildMetadataObj(metadataMap, fields);
    addMetadata(oauth2, fileId, templateKey, metadataObj);
}

/**
  * ファイル ID を config から読み出す
  * @return {String} fileId ファイル ID
  */
function decideFileId() {
    const fileId = engine.findData(configs.getObject('conf_FileId'));
    if (fileId === '' || fileId === null) {
        throw 'File ID is blank.';
    }
    return fileId;
}

/**
  * メタデータの情報を config から読み出し、Map に格納する
  * @return {Map<String, String>} metadataMap フィールド名と値の Map
  */
function retrieveMetadataMap() {
    const metadataMap = new Map();
    for (let i = 0; i < FIELD_NUM; i++) {
        const fieldName = configs.get(`conf_FieldName${i+1}`);
        const fieldValue = configs.get(`conf_FieldValue${i+1}`);
        if (fieldName === '' || fieldName === null) { // フィールド名が空
            if (fieldValue === '' || fieldValue === null) { // 値も空
                continue;
            }
            throw `Value for Field ${i+1} is set but its display name is blank.`;
        }
        if (fieldValue === '' || fieldValue === null) { // 値が空
            continue;
        }
        if (metadataMap.has(fieldName)) { // フィールド名の指定が重複
            throw `The Field Name "${fieldName}" is set multiple times.`;
        }
        metadataMap.set(fieldName, fieldValue);
    }
    return metadataMap;
}

/**
  * テンプレートを表示名で検索し、一致した非表示でないテンプレートを返す
  * 同名のテンプレートが複数ある場合はエラー
  * @param {String} oauth OAuth2 設定
  * @param {String} templateName テンプレートの表示名
  * @return {Object} template テンプレートオブジェクト
  */
function searchTemplate(oauth2, templateName) {
    const url = 'https://api.box.com/2.0/metadata_templates/enterprise';
    const response = httpClient.begin()
        .authSetting(oauth2)
        .queryParam('limit', '500') // 現時点で企業内に登録できるテンプレート数は 500 まで
        .get(url);
    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) {
        engine.log(responseStr);
        throw `Failed to search template. status:${status}`;
    }
    const {entries} = JSON.parse(responseStr);
    // 名前が一致し、かつ hidden パラメータが false のものを検索
    const result = entries.filter(template => {
        return (template.displayName === templateName) && !template.hidden;
    });
    if (result.length === 0) {
        throw `Template with display name "${templateName}" not found.`;
    }
    if (result.length > 1) {
        throw `Multiple templates with display name "${templateName}" found.`;
    }
    return result[0];
}

/**
  * メタデータオブジェクトを生成する
  * フィールド一覧を表示名で検索し、一致した非表示でないフィールドのキーを使用する
  * 同名のフィールドが複数ある場合はエラー
  * フィールドの型と値の書式が一致しない場合はエラー
  * @param {Map<String, String>} metadataMap フィールド名と値の Map
  * @param {Array<Object>} fields フィールド一覧
  * @return {Object} metadataObj メタデータオブジェクト
  */
function buildMetadataObj(metadataMap, fields) {
    const metadataObj = {};
    metadataMap.forEach((fieldValue, fieldName) => {
        // 名前が一致し、かつ hidden パラメータが false のものを検索
        const result = fields.filter(field => {
            return (field.displayName === fieldName) && !field.hidden;
        });
        if (result.length === 0) {
            throw `Field with display name "${fieldName}" not found.`;
        }
        if (result.length > 1) {
            throw `Multiple fields with display name "${fieldName}" found.`;
        }
        const field = result[0];
        let value = fieldValue;
        // フィールドの型とデータ項目の型の一致を確認し、値を整形する
        switch (field.type) {
            case 'string':
                break;
            case 'float':
                value = checkAndParseFloat(fieldName, value);
                break;
            case 'date':
                value = checkAndParseDate(fieldName, value);
                break;
            case 'enum':
                checkOptions(fieldName, value, field.options);
                break;
            case 'multiSelect': // 1 つだけ選択する場合にのみ対応
                checkOptions(fieldName, value, field.options);
                value = Array.of(value);
                break;
             default: // 現状はありえないが、Box のメタデータのフィールド型が追加された場合に発生
                throw `The type of "${fieldName}" is not supported: ${field.type}`;
        }
        // オブジェクトに追加
        metadataObj[field.key] = value;
    });
    return metadataObj;
}

/**
  * 文字列を数値に変換
  * マイナス記号と小数点以外の文字が含まれる場合はエラー
  * @param {String} fieldName フィールド名(エラー出力用)
  * @param {String} value 入力文字列
  * @return {Number} parsedValue 数値
  */
function checkAndParseFloat(fieldName, value) {
    const regex = /^\-?(\d|[1-9]\d+)(\.\d+)?$/;
    if (!regex.test(value)) {
        throw `The type of "${fieldName}" is float, but the value "${value}" is invalid as float.`;
    }
    // 文字列を float にパース
    return parseFloat(value);
}

/**
  * 文字列を yyyy-MM-dd'T'HH:mm:ssZ 形式の文字列に変換
  * 入力が yyyy-MM-dd 形式の日付でない場合はエラー
  * @param {String} fieldName フィールド名(エラー出力用)
  * @param {String} value 入力文字列
  * @return {String} parsedValue 指定形式の文字列
  */
function checkAndParseDate(fieldName, value) {
    const regex = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
    const matched = value.match(regex);
    if (matched === null) { // yyyy-MM-dd 形式でない
        throw `The type of "${fieldName}" is date, but the value "${value}" is not in the format of "yyyy-MM-dd".`;
    }
    // 日付として不正でないかをチェック
    // Date にパースするだけでは6月31日なども許可されてしまうので、年月日それぞれをチェック
    const y = parseInt(matched[1]);
    const m = parseInt(matched[2]) -1;
    const d  = parseInt(matched[3]);
    const date = new Date(y, m, d);
    if (y !== date.getFullYear() || m !== date.getMonth() || d !== date.getDate()) {
        throw `The type of "${fieldName}" is date, but the value "${value}" is invalid as date.`;
    }
    return `${value}T00:00:00Z`;
}

/**
  * 入力文字列が選択肢として存在するかチェックし、存在しない場合はエラー
  * 選択肢の表示名(キー)が重複することは、Box の仕様としてありえない
  * @param {String} fieldName フィールド名(エラー出力用)
  * @param {String} value 入力文字列
  * @param {Array<Object>} options 選択肢一覧
  */
function checkOptions(fieldName, value, options) {
    if (!options.some(option => option.key === value)) {
        throw `The value "${value}" is not in the options of the field "${fieldName}".`;
    }
}

/**
  * メタデータを追加
  * @param {String} oauth OAuth2 設定
  * @param {String} fileId ファイル ID
  * @param {String} templateKey テンプレートキー
  * @param {Object} metadataObj 追加するメタデータ
  */
function addMetadata(oauth2, fileId, templateKey, metadataObj) {
    const url = `https://api.box.com/2.0/files/${encodeURIComponent(fileId)}/metadata/enterprise/${encodeURIComponent(templateKey)}`;
    const response = httpClient.begin()
        .authSetting(oauth2)
        .body(JSON.stringify(metadataObj), 'application/json; charset=UTF-8')
        .post(url);
    const status = response.getStatusCode();
    if (status !== 201) {
        engine.log(response.getResponseAsString());
        throw `Failed to add metadata. status:${status}`;
    }
}

    
%d bloggers like this: