kintone: レコード取得 (kintone: Get Record)
この工程は、kintone アプリのレコード(1件)の内容を取得します。
Configs:共通設定
  • 工程名
  • メモ
Configs
  • C1: API トークンを設定した認証設定 *
  • C2: Basic 認証設定(kintone で設定されている場合のみ)
  • C3: ドメイン(xxxxx.kintone.com または xxxxx.cybozu.com) *
  • C4: ゲストスペース ID(ゲストスペース内のアプリの場合のみ)
  • C5: アプリ ID *
  • C6: レコード ID *
  • C7F: フィールドコード_1
  • C7V: 値_1 を保存するデータ項目
  • C8F: フィールドコード_2
  • C8V: 値_2 を保存するデータ項目
  • C9F: フィールドコード_3
  • C9V: 値_3 を保存するデータ項目
  • C10F: フィールドコード_4
  • C10V: 値_4 を保存するデータ項目
  • C11F: フィールドコード_5
  • C11V: 値_5 を保存するデータ項目
  • C12F: フィールドコード_6
  • C12V: 値_6 を保存するデータ項目
  • C13F: フィールドコード_7
  • C13V: 値_7 を保存するデータ項目

Notes

  • kintone アプリの API トークンを取得するには、アプリの設定画面の「設定(App Settings)」のタブを開き、「API トークン(API Token)」へと進みます

    「生成する(Generate)」をクリックし、権限(Permissions)を選択(「レコード参照(View records)」権限が必要です)したあと、「保存(Save)」をクリックします

    「アプリを更新(Update App)」をクリックして変更を適用するのも忘れないようにしてください
  • kintone アプリのゲストスペース ID(アプリがゲストスペースにある場合)とアプリ ID は、API トークンの設定画面で確認することができます
  • このモデリング要素がサポートするフィールド型は、レコード番号レコード IDリビジョンステータス作成日時更新日時文字列(1行)数値計算文字列(複数行)リッチエディターラジオボタンドロップダウンリンク日付時刻日時ルックアップです
  • 各データ型に保存できるフィールド型は下表の通りです:
データ型フィールド型
文字サポートされているフィールド型すべて
数値数値計算(値が数値型の場合のみ)、ルックアップ(値が数値型の場合のみ)
選択ラジオボタンドロップダウン
※ フィールドの値は、一致する選択肢 ID をもつ選択肢として保存されます
日付日付計算(値が日付型の場合のみ)
日時作成日時更新日時日時計算(値が日時型の場合のみ)

Capture

See also

Script (click to open)
  • 下記のスクリプトを記述した XML ファイルをダウンロードできます
    • kintone-record-get.xml (C) Questetra, Inc. (MIT License)
    • Professional をご利用であればファイルの内容を改変することでオリジナルのアドオンとして活用できます


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

main();
function main(){
  //// == 工程コンフィグ・ワークフローデータの参照 / Config & Data Retrieving ==
  const auth = configs.get("conf_auth");
  const basic = configs.get("conf_basic");
  const domain = configs.get("conf_domain");
  const guestSpaceId = configs.get("conf_guestSpaceId");
  const appId = configs.get("conf_appId");
  const recordId = retrieveRecordId();

  const fieldCodeList = [];
  const fieldValueDefList = [];
  retrieveFieldConfigs( fieldCodeList, fieldValueDefList );

  const apiToken = httpClient.getOAuth2Token( auth );

  //// == 演算 / Calculating ==
  const apiUri = determineApiUri( domain, guestSpaceId, appId, recordId );
  const fieldValueList = [];
  const fieldTypeList = [];
  getRecord( apiUri, apiToken, basic, fieldCodeList, fieldValueList, fieldTypeList );

  //// == ワークフローデータへの代入 / Data Updating ==
  setDataByLists( fieldValueDefList, fieldValueList, fieldTypeList );
}

/**
  * config からレコード ID を読み出す
  * @return {String} recordId  レコード ID
  */
function retrieveRecordId() {
  const recordIdDef = configs.getObject( "conf_recordId" );
  if ( recordIdDef.matchDataType("SELECT_SINGLE") ) { // 選択型データ項目の場合
    const select = engine.findData( recordIdDef );
    if ( select === null || select.size() === 0 ) { // 未選択
      throw "Record ID is not selected.";
    }
    return select.get(0).getValue();
  }
  return engine.findData( recordIdDef ); // 文字型データ項目の場合
}

/**
  * config のフィールド情報を読み出し、配列に格納する
  * 以下の場合はエラーとする
  * 1. フィールドコードが空で、値を保存するデータ項目が設定されている
  * 2. フィールドコードが設定されていて、値を保存するデータ項目が設定されていない
  * 3. 値を保存するデータ項目が重複して設定されている
  * 4. フィールドコードが一つも設定されていない
  * @param {Array<String>} fieldCodeList  フィールドコードを格納する配列
  * @param {Array<ProcessDataDefinitionView>} fieldValueDefList  フィールドの値を格納するデータ項目の ProcessDataDefinitionView を格納する配列
  */
function retrieveFieldConfigs( fieldCodeList, fieldValueDefList ) {
  const dataItemNumList = []; // データ項目の重複確認用
  for (let i = 0; i < FIELD_NUM; i++) {
    const fieldCodeConfigName = `conf_fieldCode${i+1}`;
    const fieldValueConfigName = `conf_fieldValue${i+1}`;
    const fieldCode = configs.get( fieldCodeConfigName );
    const fieldValueDef = configs.getObject( fieldValueConfigName );
    const dataItemNum = configs.get( fieldValueConfigName ); // データ項目の重複確認用
    if ( fieldCode === "" || fieldCode === null ) { // フィールドコードが空
      if ( fieldValueDef !== null ) { // 値を保存するデータ項目が設定されている
        throw `Field Code ${i+1} is empty but Data item that will save Value ${i+1} is set.`;
      } else { // 値を保存するデータ項目が設定されていない
        continue;
      }
    } else { // フィールドコードが設定されている
      if ( fieldValueDef === null ) { // 値を保存するデータ項目が設定されていない
        throw `Data item to save the value of ${fieldCode} is not set.`;
      }
    }
    if ( dataItemNumList.indexOf( dataItemNum ) !== -1 ) { // 既に指定されているデータ項目
      throw "The same data item is set multiple times.";
    }
    fieldCodeList.push( fieldCode );
    fieldValueDefList.push( fieldValueDef );
    dataItemNumList.push( dataItemNum ); // データ項目の重複確認用
  }
  if ( fieldCodeList.length === 0 ) { // フィールドコードが一つも設定されていない
    throw "No Field Code is set.";
  }
}

/**
  * kintone REST API のレコード取得の URI を決定する
  * ドメインが空、または kintone のドメインとして不正な文字列であればエラーとする
  * @param {String} domain  ドメイン
  * @param {String} guestSpaceId  ゲストスペース ID
  * @param {String} appId  アプリ ID
  * @param {String} recordId  レコード ID
  * @return {String} apiUri  API の URI
  */
function determineApiUri( domain, guestSpaceId, appId, recordId ) {
  checkDomainAndIds( domain, appId, recordId );
  let apiUri = "";
  if ( guestSpaceId === "" || guestSpaceId === null ) {
    apiUri = `https://${domain}/k/v1/record.json?app=${appId}&id=${recordId}`;
  } else {
    if ( !isValidId(guestSpaceId) ) {
      throw "Invalid Guest Space ID.";
    }
    apiUri = `https://${domain}/k/guest/${guestSpaceId}/v1/record.json?app=${appId}&id=${recordId}`;
  }
  return apiUri;
}

/**
  * ドメイン、アプリ ID、レコード ID が空または不正な文字列であればエラーとする
  * @param {String} domain  ドメイン
  * @param {String} appId  アプリ ID
  * @param {String} recordId  レコード ID
  */
function checkDomainAndIds( domain, appId, recordId ) {
  if ( domain === "" || domain === null ) { // required="true" なので空になることはないが、チェック
    throw "Domain is empty.";
  }
  const reg = new RegExp( '^[0-9a-zA-Z-]{3,32}.(?:kintone.com|cybozu.com)$' );
  if ( !reg.test(domain) ) {
    throw "Invalid Kintone domain.";
  }
  if ( appId === "" || appId === null ) { // required="true" なので空になることはないが、チェック
    throw "App ID is empty.";
  }
  if ( !isValidId(appId) ) {
    throw "Invalid App ID.";
  }
  if ( recordId === "" || recordId === null ) { // required="true" だが、データ項目の値が空の可能性がある
    throw "Record ID is empty.";
  }
  if ( !isValidId(recordId) ) {
    throw "Invalid Record ID.";
  }
}

/**
  * ID が有効か(自然数か)を判定する
  * @param {String} idString  ID の文字列
  * @return {Boolean}  有効な ID かどうか
  */
function isValidId( idString ) {
  const idReg = new RegExp( '^[1-9][0-9]*$' );
  return idReg.test( idString );
}

/**
  * kintone REST API にレコード取得の GET リクエストを送信する
  * @param {String} apiUri  API の URI
  * @param {String} apiToken  API トークン
  * @param {String} basic  Basic 認証設定
  * @param {Array<String>} fieldCodeList  フィールドコードが格納された配列
  * @param {Array<String>} fieldValueList  フィールドの値を格納する配列
  * @param {Array<String>} fieldTypeList  フィールドの型を格納する配列
  */
function getRecord( apiUri, apiToken, basic, fieldCodeList, fieldValueList, fieldTypeList ) {
  let request = httpClient.begin()
    .header( "X-Cybozu-API-Token", apiToken );
  if ( basic !== "" && basic !== null ) {
    request = request.authSetting(basic);
  }
  //when error thrown
  const response = request.get( apiUri );
  const responseJson = response.getResponseAsString();
  const status = response.getStatusCode();
  if (status >= 300) {
    engine.log(`API URI: ${apiUri}`);
    const accessLog = `---GET request--- ${status}\n${responseJson}\n`;
    engine.log(accessLog);
    throw `Failed to get record. status: ${status}`;
  }
  const json = JSON.parse(responseJson);
  extractFieldValues( json.record, fieldCodeList, fieldValueList, fieldTypeList );
}

/**
  * レコード情報の JSON オブジェクトからフィールドコードに対応する値を読み出し、配列に格納する
  * @param {Object} recordObj  レコード情報の JSON オブジェクト
  * @param {Array<String>} fieldCodeList  フィールドコードが格納された配列
  * @param {Array<String>} fieldValueList フィールドの値を格納する配列
  * @param {Array<String>} fieldTypeList  フィールドの型を格納する配列
  */
function extractFieldValues( recordObj, fieldCodeList, fieldValueList, fieldTypeList ) {
  for ( const i in fieldCodeList ) {
    if ( recordObj[fieldCodeList[i]] === undefined ) { // レコード情報に一致するフィールドコードがない場合、エラー
      throw `${fieldCodeList[i]} does not exist in the record.`;
    }
    const fieldValueObj = recordObj[fieldCodeList[i]].value;
    const fieldType = recordObj[fieldCodeList[i]].type;
    if ( fieldValueObj === null ) { // null であれば、値の配列に空文字列を push する
      fieldValueList.push( "" );
      fieldTypeList.push( fieldType );
    } else if ( typeof fieldValueObj === 'string' ) { // String であれば、値の配列に文字列を push する
      fieldValueList.push( fieldValueObj );
      fieldTypeList.push( fieldType );
    } else { // String 以外のクラス(Object)であればエラー
      throw `Unable to save the value of ${fieldCodeList[i]}. Field Type ${fieldType} is not supported.`;
    }
  }
}

/**
  * データ項目に出力する
  * @param {Array<ProcessDataDefinitionView>} dataDefList  保存先データ項目の ProcessDataDefinitionView が格納された配列
  * @param {Array<String>} dataStringList  出力するデータが格納された配列
  * @param {Array<String>} fieldTypeList  出力するデータのフィールド型が格納された配列
  */
function setDataByLists( dataDefList, dataStringList, fieldTypeList ) {
  if ( (dataDefList.length !== dataStringList.length) || (dataDefList.length !== fieldTypeList.length) ) {
    throw "Array length does not match.";
  }
  for (let i in dataDefList) {
    if ( dataDefList[i] !==  null ) {
      if ( dataDefList[i].matchDataType("STRING") ) { // 保存先データ項目が文字型の場合
        // 保存先データ項目が改行に対応しておらず、保存する文字列に改行が含まれる場合のエラーは QBPMS のバリデーションに任せる
        engine.setData( dataDefList[i], dataStringList[i] );
      } else if ( dataDefList[i].matchDataType("DECIMAL") ) { // 保存先データ項目が数値型の場合
        convertTypeAndSetData( dataDefList[i], dataStringList[i], fieldTypeList[i], "DECIMAL", "Numeric" );
      } else if ( dataDefList[i].matchDataType("SELECT") ) { // 保存先データ項目が選択型の場合
        convertTypeAndSetData( dataDefList[i], dataStringList[i], fieldTypeList[i], "SELECT", "Select" );
      } else if ( dataDefList[i].matchDataType("DATE") ) { // 保存先データ項目が日付型の場合
        convertTypeAndSetData( dataDefList[i], dataStringList[i], fieldTypeList[i], "DATE", "Date" );
      } else if ( dataDefList[i].matchDataType("DATETIME") ) { // 保存先データ項目が日時型の場合
        convertTypeAndSetData( dataDefList[i], dataStringList[i], fieldTypeList[i], "DATETIME", "Datetime" );
      }
    }
  }
}

/**
  * データ項目の型にしたがってデータを変換して出力する
  * 対応しないフィールド型の場合はエラーとする
  * 変換できない値の場合はエラーとする
  * @param {ProcessDataDefinitionView} dataDef  保存先データ項目の ProcessDataDefinitionView
  * @param {String} dataString  出力するデータ(変換前の文字列データ)
  * @param {String} fieldType  出力するデータのフィールド型
  * @param {String} dataType  保存先データ項目のデータ型
  * @param {String} dataTypeLabel  保存先データ項目のデータ型の表示名(エラー出力用)
  */
function convertTypeAndSetData( dataDef, dataString, fieldType, dataType, dataTypeLabel ) {
  const supportedFieldTypes = { // 保存先データ項目が文字型以外の場合にサポートするフィールド型
    "DECIMAL": [ "NUMBER", "CALC" ],
    "SELECT": [ "DROP_DOWN", "RADIO_BUTTON" ],
    "DATE": [ "DATE", "CALC" ],
    "DATETIME": [ "DATETIME", "CREATED_TIME", "UPDATED_TIME", "CALC" ]
  };
  if ( supportedFieldTypes[dataType].indexOf( fieldType ) === -1 ) { // 対応しないフィールド型の場合はエラー
    throw `The value of Field Type ${fieldType} cannot be saved to ${dataTypeLabel} type data item.`;
  }
  let convertedData;
  if ( dataString === "" || dataString === null ) { // 空値の場合は null を設定
    convertedData = null;
  } else {
    try { // CALC 等、フィールド型だけでは変換可否を判定できないものがあるので try-catch でエラーを捕捉
      switch (dataType) {
        case 'DECIMAL':
          convertedData = new java.math.BigDecimal( dataString );
          break;
        case 'SELECT': // 一致する選択肢 ID がない場合のエラーは QBPMS のバリデーションに任せる
          convertedData = new java.util.ArrayList();
          convertedData.add( dataString );
          break;
        case 'DATE':
          convertedData = java.sql.Date.valueOf( dataString );
          break;
        case 'DATETIME':
          // DateFormatWrapper#parse() で AddableTimestamp にパース
          convertedData = dateFormatter.parse("yyyy-MM-dd'T'HH:mm:ssX", dataString);
          break;
      }
    } catch (e) { // 変換できない値の場合はエラー
      throw `Returned value "${dataString}" cannot be saved to ${dataTypeLabel} type data item.`;
    }
  }
  engine.setData( dataDef, convertedData );
}

%d人のブロガーが「いいね」をつけました。