Gmail: メール取得

Gmail: メール取得

Gmail: Get Email Message

この工程は、Gmail のメールを取得します。

Auto Step icon
Basic Configs
工程名
メモ
Configs for this Auto Step
conf_auth
C1: OAuth2 設定 *
conf_messageId
C2: メール ID *
conf_fromAddress
C3-A: From メールアドレスを保存するデータ項目
conf_fromName
C3-N: From 表示名を保存するデータ項目
conf_replyToAddresses
C4-A: Reply-To メールアドレス一覧を保存するデータ項目
conf_replyToNames
C4-N: Reply-To 表示名一覧を保存するデータ項目
conf_recipientAddresses
C5-A: To/Cc/Bcc メールアドレス一覧を保存するデータ項目
conf_recipientNames
C5-N: To/Cc/Bcc 表示名一覧を保存するデータ項目
conf_sentDatetime
C6: 送信日時を保存するデータ項目
conf_subject
C7: 件名を保存するデータ項目
conf_body
C8: 本文を保存するデータ項目
conf_attachments
C9: 添付ファイルを保存するデータ項目

Notes

  • 本モデリング要素は Google Workspace アカウント用です
    • 一般ユーザー向けアカウント(gmail.com)での利用はできません
  • 事前準備として、Google Workspace の管理者が対象の OAuth アプリを信頼できるアプリとして設定済みである必要があります
    • この設定がなされていない状態での利用はできません
    • 本モデリング要素が使用する OAuth アプリのクライアント ID は 13039123046-t87nmrj499ffoa58asehks3asajvgqnh.apps.googleusercontent.com です
  • メール ID は Gmail 上では確認できません

Capture

See also

Script (click to open)
  • 次のスクリプトが記述されている XML ファイルをダウンロードできます
    • gmail-message-get.xml (C) Questetra, Inc. (MIT License)
    • Professional のワークフロー基盤では、ファイル内容を改変しオリジナルのアドオン自動工程として活用できます


// OAuth2 config sample at [OAuth 2.0 Setting]
// - Authorization Endpoint URL: https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force
// - Token Endpoint URL: https://oauth2.googleapis.com/token
// - Scope: https://www.googleapis.com/auth/gmail.readonly
// - Consumer Key: (Get by Google Developer Console)
// - Consumer Secret: (Get by Google Developer Console)

function main(){
  //// == 工程コンフィグ・ワークフローデータの参照 / Config & Data Retrieving ==
  const auth = configs.getObject("conf_auth");
  const messageId = engine.findData( configs.getObject("conf_messageId") );
  const defs = retrieveDefs();

  //// == 演算 / Calculating ==
  const hasNoDefs = Object.values(defs).every( def => def === null );
  if ( hasNoDefs ) return; // 保存先データ項目が1つも設定されていなければ何もせず正常終了

  const apiUri = determineApiUri( messageId );
  const message = getMessage( apiUri, auth );

  const files = new java.util.ArrayList(); // もともと添付されていたファイルはクリアする
  if ( defs.attachmentsDef !== null ) {
    getAttachments( apiUri, auth, message.attachments );
    convertAndAddAttachments( message.attachments, files );
  }

  // To, Cc, Bcc の順に連結
  const recipientEmails = message.recipients.to.emails
    .concat( message.recipients.cc.emails, message.recipients.bcc.emails );
  const recipientNames = message.recipients.to.names
    .concat( message.recipients.cc.names, message.recipients.bcc.names );

  //// == ワークフローデータへの代入 / Data Updating ==
  setDataIfNotNull( defs.fromAddressDef, message.from.email );
  setDataIfNotNull( defs.fromNameDef, message.from.name );
  setDataIfNotNull( defs.replyToAddressesDef, message.replyTo.emails.join('\n') );
  setDataIfNotNull( defs.replyToNamesDef, message.replyTo.names.join('\n') );
  setDataIfNotNull( defs.recipientAddressesDef, recipientEmails.join('\n') );
  setDataIfNotNull( defs.recipientNamesDef, recipientNames.join('\n') );
  setDataIfNotNull( defs.sentDatetimeDef, new java.sql.Timestamp(Date.parse(message.datetime)) );
  setDataIfNotNull( defs.subjectDef, message.subject );
  setDataIfNotNull( defs.bodyDef, message.body );
  setDataIfNotNull( defs.attachmentsDef, files );
}

/**
  * config から保存先データ項目の ProcessDataDefinitionView を読み出す
  * 値を保存するデータ項目が重複して設定されている場合はエラーとする
  * @return {Object} defs  保存先データ項目の ProcessDataDefinitionView を格納した JSON オブジェクト
  */
function retrieveDefs() {
  const items = ["fromAddress", "fromName", "replyToAddresses", "replyToNames", 
      "recipientAddresses", "recipientNames", "sentDatetime", "subject", "body", "attachments"];
  const defs = {};
  const dataItemNumSet = new Set(); // データ項目の重複確認用
  items.forEach( item => {
    const dataItemDef = configs.getObject(`conf_${item}`);
    if ( dataItemDef !== null ) { // データ項目が設定されている場合は重複を確認する
      const dataItemNum = dataItemDef.getNumber();
      if ( dataItemNumSet.has( dataItemNum ) ) { // データ項目番号が重複していればエラー
        throw new Error("The same data item is set multiple times.");
      }
      dataItemNumSet.add( dataItemNum ); // データ項目の重複確認用
    }
    defs[`${item}Def`] = dataItemDef;
  });
  return defs;
}

/**
  * Gmail のメール取得の URI を決定する
  * メール ID が空であればエラーとする
  * @param {String} messageId  メール ID
  * @return {String} apiUri  API の URI
  */
function determineApiUri( messageId ) {
  if ( messageId === "" || messageId === null ) {
    throw new Error("Message ID is empty.");
  }
  const apiUri = `https://gmail.googleapis.com/gmail/v1/users/me/messages/${encodeURI(messageId)}`;
  return apiUri;
}

/**
  * Gmail REST API にメール取得の GET リクエストを送信し、必要な情報を JSON オブジェクトに格納する
  * @param {String} apiUri  API の URI
  * @param {String} auth  認証設定名
  * @return {Object} message  メール情報を格納した JSON オブジェクト
  */
function getMessage( apiUri, auth ) {
  const response = httpClient.begin()
    .authSetting( auth )
    .get( apiUri );

  // when error thrown
  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 new Error(`Failed to get message. status: ${status}`);
  }

  // when successful, parse the message content
  const json = JSON.parse(responseJson);
  const message = extractFromPayload( json.payload );
  return message;
}

/**
  * Gmail REST API のメール取得のレスポンスの payload フィールドから
  * 必要な情報を抜き出し、JSON オブジェクトに格納する
  * @param {Object} payload  Gmail REST API のメール取得のレスポンスの payload フィールド
  * @return {Object} message  メール情報を格納した JSON オブジェクト
  */
function extractFromPayload( payload ) {
  const message = {
    "from": {
      "email": "",
      "name": ""
    },
    "replyTo": {
      "emails": [],
      "names": []
    },
    "recipients": {
      "to": {
        "emails": [],
        "names": []
      },
      "cc": {
        "emails": [],
        "names": []
      },
      "bcc": {
        "emails": [],
        "names": []
      }
    },
    "datetime": "",
    "subject": "",
    "body": "",
    "attachments": []
  };

  // ヘッダの処理
  // From ヘッダは Gmail の仕様で必ず 1 件, Date ヘッダも 1 件
  // To, Cc, Bcc は複数件の可能性があるので配列に追加して処理
  // Subject が複数件ある場合は最初の Subject ヘッダを採用する
  payload.headers.forEach( header => {
    const headerNameLower = header.name.toLowerCase();
    switch ( headerNameLower ) {
      case 'from':
        const fromAddresses = emailService.parseAddressHeader( header.value ); // java.util.List<EmailServiceWrapper.InternetAddressWrapper>
        if ( fromAddresses !== null && fromAddresses.length !== 0 ) {
          message.from.email = fromAddresses[0].getAddress();
          message.from.name = fromAddresses[0].getPersonal();
        }
        break;
      case 'reply-to':
        const replyToAddresses = emailService.parseAddressHeader( header.value ); // java.util.List<EmailServiceWrapper.InternetAddressWrapper>
        if ( replyToAddresses !== null && replyToAddresses.length !== 0 ) {
          replyToAddresses.forEach( addr => {
            message.replyTo.emails.push( addr.getAddress() );
            message.replyTo.names.push( addr.getPersonal() );
          });
        }
        break;
      case 'to':
      case 'cc':
      case 'bcc':
        const addresses  = emailService.parseAddressHeader( header.value ); // java.util.List<EmailServiceWrapper.InternetAddressWrapper>
        if ( addresses !== null && addresses.length !== 0 ) {
          addresses.forEach( addr => {
            message.recipients[headerNameLower].emails.push( addr.getAddress() );
            message.recipients[headerNameLower].names.push( addr.getPersonal() );
          });
        }
        break;
      case 'date':
        message.datetime = header.value;
        break;
      case 'subject':
        if ( message.subject === "" ) { // 最初の Subject ヘッダを採用する
          message.subject = header.value;
        }
        break;
    }
  });

  // パート(本文、添付ファイル)の処理
  const mimeType = payload.mimeType;
  if ( mimeType === "text/plain" || mimeType === "text/html" ) { // 本文のみのメール
    message.body = parseTextPart( payload );
  } else if ( mimeType.startsWith("multipart/") ) { // マルチパートのメール
    const bodyParts = [];
    parseMultiPart( payload, bodyParts, message.attachments );
    if ( bodyParts.length === 0 ) {
      engine.log("No body part was found.");
      message.body = "";
    } else {
      message.body = bodyParts.map( p => parseTextPart(p) ).join('\n'); // Gmail の UI では <wbr> で区切られている
    }
  } else {
    throw new Error(`Unsupported MIME type. ${mimeType}`);
  }

  return message;
}

/**
  * MIME タイプが text/plain, text/html のパートのデータをデコードして文字列として返す
  * @param {Object} part  MIME メールのパート(text/plain または text/html)
  * @return {String} body  パートのデータ(メール本文として保存する文字列)
  */
function parseTextPart( part ) {
  const bodyData = part.body.data ?? '';
  return base64.decodeFromUrlSafeString( bodyData ); // UTF-8 としてデコード
}

/**
 * MIME タイプが multipart/* のパートを再帰的に解析する
 * 本文パートは bodyParts に、添付ファイルは attachments に格納する
 * @param {Object} part  MIME メールのパート(multipart/*)
 * @param {Array<Object>} bodyParts  本文パートを格納する配列
 * @param {Array<Object>} attachments  添付ファイルの情報を格納する配列
 */
function parseMultiPart( part, bodyParts, attachments ) {
  engine.log(`Multipart found. partId: ${part.partId}, mimeType: ${part.mimeType}`);
  if (part.parts === undefined || part.parts.length === 0) {
    engine.log(`No inner-part was found in this multipart. partId: ${part.partId}`);
    return;
  }

  if ( part.mimeType === 'multipart/alternative' ) {
    parseMultiPartAlternative( part, bodyParts, attachments );
    return;
  }
  if ( part.mimeType === 'multipart/related' ) {
    parseMultiPartRelated( part, bodyParts, attachments );
    return;
  }
  // multipart/mixed およびその他の multipart/* はデフォルト処理
  parseMultiPartMixed( part, bodyParts, attachments );
}

/**
 * multipart/alternative を解析する
 * 各パートは同じ内容の異なる表現であるため、採用した1グループだけを bodyParts に push する。
 * 優先順位: text/plain > text/html(multipart 内から取得した本文も含む)
 * Gmail の UI とは異なり、できるだけ簡易な本文を採用したいので、先頭から走査していく。
 * 本文パートとして採用されうるのは、明示的に添付ファイルでないパートのみ。
 * 本文パートとして採用されなかったパートは基本的には無視されるが、
 * 以下のパートは、例外的に添付ファイルとして保存される。(Gmail の仕様と揃えている)
 * - multipart/alternative 内でさらに multipart/* がある場合、その multipart で添付ファイルと判定されたパート
 * - ファイル名が設定されているパート
 * @param {Object} part
 * @param {Array<Object>} bodyParts
 * @param {Array<Object>} attachments
 */
function parseMultiPartAlternative( part, bodyParts, attachments ) {
  let textParts = []; // text/plain のパート群
  let htmlParts = []; // text/html のパート群

  part.parts.forEach( innerPart => {
    if ( innerPart.mimeType.startsWith('multipart/') ) {
      const nestedBodyParts = [];
      parseMultiPart( innerPart, nestedBodyParts, attachments ); // 添付ファイルが追加されうる
      if ( nestedBodyParts.length === 0 ) {
        return;
      }
      if ( textParts.length === 0 && nestedBodyParts.every( p => p.mimeType === 'text/plain' )) {
        nestedBodyParts.forEach( p => outputLogBodyCandidatePart(p) );
        textParts = nestedBodyParts;
      } else if ( htmlParts.length === 0 ) {
        nestedBodyParts.forEach( p => outputLogBodyCandidatePart(p) );
        htmlParts = nestedBodyParts;
      } else {
        nestedBodyParts.forEach( p => outputLogIgnoredPart(p) );
      }
      return;
    }
    if ( !isObviouslyAttachment(innerPart) && innerPart.mimeType === 'text/html' ) {
      if ( htmlParts.length === 0 ) {
        outputLogBodyCandidatePart( innerPart );
        htmlParts = [innerPart];
      } else {
        outputLogIgnoredPart( innerPart );
      }
      return;
    }
    if ( !isObviouslyAttachment(innerPart) && innerPart.mimeType === 'text/plain' ) {
      if ( textParts.length === 0 ) {
        outputLogBodyCandidatePart( innerPart );
        textParts = [innerPart];
      } else {
        outputLogIgnoredPart( innerPart );
      }
      return;
    }
    // alternative 直下のパートは、ファイル名がある場合のみ添付ファイルとして扱う
    if ( !isFileNameBlank( innerPart ) ) {
      addPartAsAttachment( innerPart, attachments );
      return;
    }
    // それ以外のパートは、ログだけ残して無視
    outputLogIgnoredPart( innerPart );
  });

  // 本文パートを選択
  // 優先順位: text/plain > text/html
  // 採用グループの全パートを push し、落選グループはログに残す
  const selected  = textParts.length > 0 ? textParts : htmlParts;
  const discarded = textParts.length > 0 ? htmlParts : [];
  if ( selected.length === 0 ) {
    engine.log(`No body part was found in multipart/alternative. partId: ${part.partId}`);
    return;
  }
  bodyParts.push( ...selected );
  discarded.forEach( p => outputLogIgnoredPart(p) );
}

/**
 * multipart/related を解析する
 * 本文パートを 1 件だけ選択し、それ以外の直下のパートは添付ファイルとして扱う(ファイル名がなければ noname)
 * 本文パート候補は上から順に探す:
 *   - 明示的に添付ファイルでない text/plain または text/html → そのパートが本文
 *   - 本文未確定の段階で multipart が現れた場合 → その multipart 自身の本文選択ロジックで解析し本文と添付ファイルを追加
 *   - 本文確定後の multipart → 中の全パートを再帰的に添付ファイルとして処理
 * @param {Object} part
 * @param {Array<Object>} bodyParts
 * @param {Array<Object>} attachments
 */
function parseMultiPartRelated( part, bodyParts, attachments ) {
  let bodyFound = false;

  part.parts.forEach( innerPart => {
    if ( bodyFound ) { // すでに本文パートが見つかっている場合
      if ( innerPart.mimeType.startsWith('multipart/') ) {
        addAllPartsAsAttachments( innerPart, attachments );
      } else {
        addPartAsAttachment( innerPart, attachments );
      }
      return;
    }

    // まだ本文パートが見つかっていない場合
    if ( innerPart.mimeType.startsWith('multipart/') ) {
      const tempBodyParts = [];
      parseMultiPart( innerPart, tempBodyParts, attachments );
      if ( tempBodyParts.length > 0 ) {
        bodyParts.push( ...tempBodyParts );
        bodyFound = true;
      }
      return;
    }
    if ( !isObviouslyAttachment(innerPart) && ( innerPart.mimeType === 'text/plain' || innerPart.mimeType === 'text/html' ) ) {
      bodyParts.push( innerPart );
      bodyFound = true;
      return;
    }
    addPartAsAttachment( innerPart, attachments );
  });

  if ( !bodyFound ) {
    engine.log(`No body part was found in multipart/related. partId: ${part.partId}`);
  }
}

/** 保存時のファイル名の最大長 */
const FILE_NAME_MAX_LENGTH = 200;

/**
 * パートを添付ファイルとして attachments に追加する
 * ファイル名が設定されていなければ noname を使う
 * ファイル名が長すぎる場合は省略する
 * @param {Object} part  MIME メールのパート
 * @param {Array<Object>} attachments  添付ファイルの情報を格納する配列
 */
function addPartAsAttachment( part, attachments ) {
  let filename = isFileNameBlank( part ) ? 'noname' : part.filename;
  if ( filename.length > FILE_NAME_MAX_LENGTH ) {
    filename = filename.substring(0, FILE_NAME_MAX_LENGTH - 3) + "...";
  }
  attachments.push({
    filename:    filename,
    contentType: getContentType( part ),
    body:        part.body
  });
}

/**
 * multipart の中のすべてのパートを再帰的に添付ファイルとして処理する(ファイル名がなければ noname)
 * multipart/related の本文確定後に現れた multipart の処理に使う
 * @param {Object} part  multipart/* のパート
 * @param {Array<Object>} attachments  添付ファイルの情報を格納する配列
 */
function addAllPartsAsAttachments( part, attachments ) {
  if ( part.parts === undefined || part.parts.length === 0 ) {
    return;
  }
  part.parts.forEach( innerPart => {
    if ( innerPart.mimeType.startsWith('multipart/') ) {
      addAllPartsAsAttachments( innerPart, attachments );
    } else {
      addPartAsAttachment( innerPart, attachments );
    }
  });
}

/**
 * multipart/mixed およびその他の multipart/* を解析する
 * 本文パート(text/plain, text/html)は全て順番に bodyParts に push する
 * 添付ファイルは attachments に push する
 * @param {Object} part
 * @param {Array<Object>} bodyParts
 * @param {Array<Object>} attachments
 */
function parseMultiPartMixed( part, bodyParts, attachments ) {
  part.parts.forEach( innerPart => {
    if ( innerPart.mimeType.startsWith('multipart/') ) {
      parseMultiPart( innerPart, bodyParts, attachments );
      return;
    }
    if ( !isObviouslyAttachment(innerPart) && ( innerPart.mimeType === 'text/plain' || innerPart.mimeType === 'text/html' ) ) {
      bodyParts.push( innerPart );
      return;
    }
    // 添付ファイルとして処理
    addPartAsAttachment( innerPart, attachments );
  });
}

/**
  * multipart/alternative パース時の本文パート候補の情報をプロセスログに出力する
  * @param {Object} part  MIME メールのパート
  */
function outputLogBodyCandidatePart(part) {
  const disposition = getDisposition(part);
  engine.log(`Part ${part.partId} may be a body part. Content-Disposition: ${disposition}, mimeType: ${part.mimeType}`);
}

/**
  * 保存しないパートの情報をプロセスログに出力する
  * @param {Object} part  MIME メールのパート
  */
function outputLogIgnoredPart(part) {
  const partInfo = JSON.parse(JSON.stringify(part)); // ディープコピー
  const originalData = part.body.data ?? '';
  let extractedData = originalData.substring(0, 20); // ログ出力用に先頭 20 文字を抜粋
  if (originalData.length > 20) {
    extractedData += '...';
  }
  partInfo.body.data = extractedData;
  engine.log(`Part ${part.partId} was ignored.`);
  engine.log(JSON.stringify(partInfo));
}

/**
  * パートが明らかに添付ファイルかどうかを判定する
  * 以下のいずれかに該当すれば true を返す
  * - Content-Disposition ヘッダが "attachment"
  * - body.attachmentId がある
  * - ファイル名が設定されている
  * @param {Object} part  MIME メールのパート
  * @return {Boolean}  パートが明らかに添付ファイルかどうか
  */
function isObviouslyAttachment(part) {
  const disposition = getDisposition(part);
  if (disposition === "attachment" || part.body.attachmentId !== undefined || !isFileNameBlank(part)) {
    return true;
  }
  return false;
}

/**
  * パートのファイル名が空かどうかを判定する
  * @param {Object} part  MIME メールのパート
  * @return {Boolean}  ファイル名が空かどうか
  */
function isFileNameBlank(part) {
  return part.filename === undefined || part.filename === null || part.filename === "";
}

/**
  * MIME メールのパートの "Content-Disposition" ヘッダの値を返す
  * @param {Object} part  パート
  * @return {String} disposition  "Content-Disposition" ヘッダの値(ヘッダが無い場合は null)
  */
function getDisposition( part ) {
  if ( part.headers === undefined ) { // パートにヘッダが無い場合は null を返す
    return null;
  }
  const dispositionHeader = part.headers.find( header => header.name.toLowerCase() === "content-disposition" );
  if ( dispositionHeader === undefined ) { // 無い場合は null を返す
    return null;
  }
  return dispositionHeader.value.split(";")[0].toLowerCase();
}

/** 添付ファイルの Content-Type のデフォルト値 */
const DEFAULT_FILE_CONTENT_TYPE = "application/octet-stream";

/**
  * MIME メールのパートの "Content-Type" ヘッダの値を返す
  * ヘッダが無い場合はデフォルト値として "application/octet-stream" を返す
  * @param {Object} part  パート
  * @return {String} contentType  "Content-Type" ヘッダの値
  */
function getContentType( part ) {
  if ( part.headers === undefined ) { // パートにヘッダが無い場合はデフォルト値を返す
    return DEFAULT_FILE_CONTENT_TYPE;
  }
  const contentTypeHeader = part.headers.find( header => header.name.toLowerCase() === "content-type" );
  if ( contentTypeHeader === undefined ) { // 無い場合はデフォルト値を返す
    return DEFAULT_FILE_CONTENT_TYPE;
  }
  return contentTypeHeader.value;
}

/**
  * 添付ファイルの情報に本体データが含まれているかどうかを調べ、
  * ない場合は Gmail REST API に添付ファイル取得の GET リクエストを送信し、
  * 本体データを追加する
  * @param {String} apiUri  API の URI(/messages/{messageId} まで)
  * @param {AuthSettingWrapper} auth  OAuth2 認証設定
  * @param {Array<Object>} attachments  添付ファイルの情報(オブジェクト)が格納された配列
  *  配列内の添付ファイルの情報の形式は:
  *   {
  *     "filename": String,
  *     "contentType": String,
  *     "body": {
  *       "data": String(Base64) or "attachmentId": String
  *     }
  *   }
  */
function getAttachments( apiUri, auth, attachments ) {
  const httpLimit = httpClient.getRequestingLimit();
  let httpCount = 1; // HTTP リクエストの上限超えチェック用のカウンタ(メール取得のリクエスト後なので初期値は1)
  attachments.forEach( attachment => {
    if ( attachment.body.data !== undefined ) { // data があれば何もしない
      return;
    }
    if ( attachment.body.attachmentId === undefined ) {
      attachment.body["data"] = ""; // data も attachmentId も無い場合は空文字列をセット
      return;
    }
    httpCount++;
    if ( httpCount > httpLimit ) {
      throw new Error("Number of HTTP requests is over the limit.");
    }
    const response = httpClient.begin()
      .authSetting( auth )
      .get( `${apiUri}/attachments/${attachment.body.attachmentId}` );
    const responseJson = response.getResponseAsString();
    const status = response.getStatusCode();
    if (status >= 300) { // when error thrown
      engine.log(`API URI: ${apiUri}/attachments/${attachment.body.attachmentId}`);
      const accessLog = `---GET request--- ${status}\n${responseJson}\n`;
      engine.log( accessLog );
      throw new Error(`Failed to get attachment. status: ${status}`);
    }
    // when successful, parse the message content
    const json = JSON.parse(responseJson);
    attachment.body["data"] = json.data;
  });
}

/**
  * 添付ファイルを Qfile に変換し、ファイルの配列に追加する
  * Content-Type が不正な場合、デフォルトの Content-Type で保存する
  * @param {Array<Object>} attachments  添付ファイルの情報(オブジェクト)が格納された配列
  * @param {ListArray<Qfile>} files  ファイルの配列
  */
function convertAndAddAttachments( attachments, files ) {
  attachments.forEach( attachment => {
    let qfile;
    const bytes = base64.decodeFromUrlSafeStringToByteArray( attachment.body.data ); // ByteArrayWrapper
    try {
      qfile = new com.questetra.bpms.core.event.scripttask.NewQfile(
        attachment.filename,
        attachment.contentType,
        bytes
      );
    } catch (e) {
      engine.log(`Failed to convert attachment ${attachment.filename} to qfile. ${e.toString()}`);
      engine.log(`Trying to save it as ${DEFAULT_FILE_CONTENT_TYPE}.`);
      // Content-Type が不正な場合に例外になるので、DEFAULT_FILE_CONTENT_TYPE で作成し直す
      qfile = new com.questetra.bpms.core.event.scripttask.NewQfile(
        attachment.filename,
        DEFAULT_FILE_CONTENT_TYPE,
        bytes
      );
    }
    files.add( qfile );
  });
}

/**
  * 保存先データ項目の ProcessDataDefinitionView が null でない場合のみ値を代入する
  * @param {ProcessDataDefinitionView} def  保存先データ項目の ProcessDataDefinitionView
  * @param {Object} value  データ項目に代入する値
  */
function setDataIfNotNull( def, value ) {
  if ( def === null ) return; // データ項目が設定されていなければ何もしない
  engine.setData( def, value );
}

  

上部へスクロール

Questetra Supportをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む