開始: kintone: レコード 日時フィールド (Start: kintone: Record's Datetime Field)
kintone アプリで、レコードの日時フィールドの時刻を経過すると、プロセスが開始します。日時フィールドとして、「作成日時」「更新日時」を指定することも可能です。
Configs: 共通設定
  • 工程名
  • メモ
Configs
  • C1: API トークンを設定した認証設定 *
  • C2: Basic 認証設定(kintone で設定されている場合のみ)
  • C3: ドメイン(xxxxx.kintone.com または xxxxx.cybozu.com) *
  • C4: ゲストスペース ID(ゲストスペース内のアプリの場合のみ)
  • C5: アプリ ID *
  • C6: チェック対象とする日時フィールドのフィールドコード *
  • C7: 検索クエリ
  • C8: レコード ID を保存するデータ項目 *
  • C9: 日時データを保存するデータ項目

Notes

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

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

    「アプリを更新(Update App)」をクリックして変更を適用するのも忘れないようにしてください。
  • kintone アプリのゲストスペース ID(アプリがゲストスペースにある場合)とアプリ ID は、API トークンの設定画面で確認することができます。
  • 定期的に Questetra BPM Suite から kintone アプリにポーリングが行われ、レコードがチェックされます。検索クエリに合致し、かつ指定した日時フィールドの時刻が新たに経過したレコードがあれば、プロセスが開始されます。
  • このモデリング要素がサポートするフィールド型は、日時作成日時更新日時です。作成日時のフィールドコードを指定すると、新たに作成されたレコードについてプロセスが開始されます。更新日時のフィールドコードを指定すると、新たに更新されたレコードについてプロセスが開始されます。
  • 検索クエリで使用可能な演算子と関数については、kintone のリファレンスを参照してください。オプション(order by, limit, offset)は使用できません。
  • 初回のチェック時には、プロセスは開始されません。チェックのみ行われます。チェックの状況は、プロセスログより確認できます。
  • 短時間に多数のレコードについて指定フィールドの日時が経過すると、全てのレコードについて、プロセスは開始されません。目安は、15 分間で 90 プロセスです。

Capture

See also

Script (click to open)
  • 下記のスクリプトを記述した XML ファイルをダウンロードできます
    • kintone-record-datetime-field.xml (C) Questetra, Inc. (MIT License)
      • コードの参考用にご利用ください
      • アドオンとしてワークフローアプリにインポートすることはできません

/**
 * @typedef {Object} timestamp java.sql.Timestamp オブジェクト
 */

/** 日時フォーマッター */
const datetimeFormatter = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
/**
 * @param {string} str 日時文字列
 * @return {timestamp} java.sql.Timestamp オブジェクト
 */
const parseDatetime = str => new java.sql.Timestamp(datetimeFormatter.parse(str).getTime());

/**
 * configs から必要な情報の取り出し
 * @return {object} result 設定情報
 * @return {string} result.appId アプリ ID
 * @return {string} result.apiUrl API の URL
 * @return {string} result.datetimeField チェック対象の日時データのフィールドコード
 * @return {string} result.query 検索条件
 * @return {string} result.apiToken API トークン
 * @return {string} result.basic Basic 認証設定
 */
const prepare = () => {
    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 datetimeField = configs.get("conf_datetimeField");
    const query = configs.get("conf_query");

    checkAppId(appId);
    const apiUri = determineApiUri(domain, guestSpaceId);

    const apiToken = httpClient.getOAuth2Token(auth);

    return {
        appId,
        apiUri,
        datetimeField,
        query,
        apiToken,
        basic
    };
};

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

/**
 * ドメインが空または不正な文字列であればエラーとする
 * @param {String} domain  ドメイン
 */
const checkDomain = (domain) => {
    if (domain === "" || domain === null) {
        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.";
    }
};

/**
 * アプリ ID が空または不正な文字列であればエラーとする
 * @param {String} appId  アプリ ID
 */
const checkAppId = (appId) => {
    if (appId === "" || appId === null) {
        throw "App ID is empty.";
    }
    if (!isValidId(appId)) {
        throw "Invalid App ID.";
    }
};

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

/**
 * レコードの検索
 * @param limit レコード数の上限
 * @param timestampLowerLimit timestamp の下限
 * @return {Array} records レコード一覧
 */
const list = (limit, timestampLowerLimit) => {
    const {
        appId,
        apiUri,
        datetimeField,
        query,
        apiToken,
        basic
    } = prepare();

    const records = getRecords(apiUri, apiToken, basic, {
        appId,
        datetimeField,
        query,
        limit,
        timestampLowerLimit
    });
    logRecords(records);
    return records;
};

/**
 * レコードのログ出力
 * @param {Array} records レコード一覧
 */
const logRecords = (records) => {
    const replacer = (key, value) => value instanceof java.sql.Timestamp ? datetimeFormatter.format(value) : value;
    records.forEach(record => {
        engine.log(JSON.stringify(record, replacer));
    });
};

/**
 * kintone REST API にレコード取得の GET リクエストを送信する
 * @param {String} apiUri  API の URI
 * @param {String} apiToken  API トークン
 * @param {String} basic  Basic 認証設定
 * @param {Object} params API リクエストのパラメータに使用する情報が格納されたオブジェクト
 * @param {string} params.appId アプリ ID
 * @param {string} params.datetimeField 日時データのフィールドコード
 * @param {string} params.query 検索条件
 * @param {number} params.limit 結果件数の上限
 * @param {timestamp} params.timestampLowerLimit timestamp の下限
 * @return {Array} records レコード一覧
 * @return {string} records[].id レコードID + 日時データ
 * @return {string} records[].recordId レコードID
 * @return {timestamp} records[].timestamp レコードの日時データ
 */
const getRecords = (apiUri, apiToken, basic, {
    appId,
    datetimeField,
    query,
    limit,
    timestampLowerLimit
}) => {
    const getRecordsUri = `${apiUri}records.json`;
    engine.log(`API URI: ${getRecordsUri}`);
    engine.log(`appId: ${appId}`);

    let request = httpClient.begin()
        .header("X-Cybozu-API-Token", apiToken)
        .queryParam('app', appId);
    if (basic !== "" && basic !== null) {
        request = request.authSetting(basic);
    }

    let queryString = `${datetimeField} >= "${datetimeFormatter.format(timestampLowerLimit)}" and ${datetimeField} <= NOW()`;
    if (query !== '') {
        queryString = `(${query}) and ${queryString}`;
    }
    queryString = `${queryString} order by ${datetimeField} desc, $id desc limit ${limit}`;
    engine.log(`query: ${queryString}`);
    request = request.queryParam('query', queryString);
    request = request.queryParam('fields[0]', '$id');
    request = request.queryParam('fields[1]', datetimeField);
    const response = request.get(getRecordsUri);

    //when error thrown
    const responseJson = response.getResponseAsString();
    const status = response.getStatusCode();
    if (status >= 300) {
        const accessLog = `---GET request--- ${status}\n${responseJson}\n`;
        engine.log(accessLog);
        throw `Failed to get records. status: ${status}`;
    }
    const json = JSON.parse(responseJson);

    // レコードオブジェクトを整形して返す
    const formatRecord = (record) => {
        const recordId = String(record['$id']['value']);
        const timestamp = parseDatetime(record[datetimeField]['value']);
        // recordId と timestamp の組み合わせを id に。日時データの変更に対応するため。
        const id = `${recordId}_${timestamp.getTime()}`;
        return {
            id,
            recordId,
            timestamp
        };
    };

    return json.records.map(formatRecord);
};

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