START_EVENT 2025-10-06 (C) Questetra, Inc. (MIT License) 3 2 This item starts a process when Gmail has received a new message. このアイテムは、Gmail が新しいメールを受信すると、プロセスを開始します。 https://support.questetra.com/bpmn-icons/start-event-gmail-message-received/ https://support.questetra.com/ja/bpmn-icons/start-event-gmail-message-received/ iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADPUlEQVRYR8WXX0hTURzHv2dzy5bb 1HKKRH+gtx7K2izooXI+BVH41F+IglAJmhVICr2kgRG6DFRERXBpGIgkZA/Oeigir+VzIGQE1bTU yXYdy90Tu+5u997dea9p3PO2nd+fz/md359zCXReRKv/76cPW4ysoYKAnARoCSj2gCCP16dYAMEM QKYo6OuYhRsqHvnIarGtCjB7wlWELNSCohJAthajACIg6MAKmhxvmJ9r6awJECgrrSSEPgKwTaNj uViYUnKncHyiI5N+RoC5clcbpaj6R8cSNULQXjDGVCvZUgQIlDn7CSHnN8O5YINSOlA4PnlBbjMN YDNPnuZMIRISgMSdt2/myeW2KCVV4pxIAvDZbsS0kHCGAgdsdQ0w2HPBPvchMvpCMxexWmGrb4Sx qBiRl8NgB31i3TBi2CdURwqg3NUCCo8gme8b5g0IK8q8R7C+BuC4NUFMJU7YG70gZnNSLnj3JuL6 yUXgdYwxNfHfPEC8yWSxht/iOi949Q7IMkmcccFFLHquI/ZtRhEip/oWtlacS9tjB/sQ7nwi/j+y YuG2x5sVDxBwuy4RoE8soQTA71MOodaHWB4ZSh3IakWetwvG3XsVweJXEO5slexR4HKhn/HxALPu 0m6AXtUEkBASrsR80AlbQ7Mk5HIKJQCA9Dj8E9cSAK5PAErWA8AHg2VBLBbV5FQGwJTDzxxaBShz zScHS8JcxitQdZcuoAhAseAYZ/KFCFC5WhoApQBRnV2AglyGCMDhZ1YtzrpdqgCxwA+w/b2wemoB YlCMQ+zrF4Sf9sBWd1+yrw6g4QriAPMXz8C4cxdyH3fxDUq8loeeIdTWDPORY7A3tqgDyK5ANQkF AN6ywQD7Ay/MzqOg0SiW7t1GdPIDv6UZAOIk1FCGEoDE+bYcd+PP1CS4pWDyxNoBRGWopREpASgl glYASSNSasU7Rt+CmFL9fEMAA70Id7eJeaWtmK8E2TDKqfIg+9TZZOmtTH/m54DaMu0/AHtTqu3S UAgLN66A+zWXUpUPIx5ANo7VHG1gX3kcxw3q+iARTqTrk0yA0PVR+j8ise5neSoSOn6YCBC6fpqJ S023j9MN1Lsm1b9qWLIw6P4tOAAAAABJRU5ErkJggg== { const auth = httpClient.createAuthSettingOAuth2( 'Google', 'https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force', 'https://accounts.google.com/o/oauth2/token', 'gmail', 'consumer_key', 'consumer_secret', 'access_token' ); configs.putObject('conf_auth', auth); configs.put('conf_label', label); }; /** * ラベル一覧 を取得する GET リクエストのテスト * @param {Object} request * @param request.url * @param request.method */ const assertGetLabelListRequest = ({url, method}) => { expect(url).toEqual('https://gmail.googleapis.com/gmail/v1/users/me/labels'); expect(method).toEqual('GET'); }; /** * メール一覧を取得する GET リクエストのテスト * @param {Object} request * @param request.url * @param request.method * @param labelIds * @param limit * @param timestampLowerLimit */ const assertGetMessageListRequest = ({url, method}, labelIds, limit, timestampLowerLimit) => { let expectedUrl = 'https://gmail.googleapis.com/gmail/v1/users/me/messages' + `?maxResults=${limit}`; labelIds.forEach(labelId => { expectedUrl += `&labelIds=${labelId}`; }); const q = `newer: ${Math.floor(timestampLowerLimit.getTime() / 1000)}`; expectedUrl += `&q=${encodeURIComponent(q)}`; expectedUrl = expectedUrl.replace(/%20/g, '+'); // HttpRequestWrapper#queryParam() はスペースを + に置き換える; expect(url).toEqual(expectedUrl); expect(method).toEqual('GET'); }; /** * メールを取得する GET リクエストのテスト * @param {Object} request * @param request.url * @param request.method * @param messageId */ const assertGetMessageRequest = ({url, method}, messageId) => { const expectedUrl = `https://gmail.googleapis.com/gmail/v1/users/me/messages/${messageId}` + '?format=metadata'; expect(url).toEqual(expectedUrl); expect(method).toEqual('GET'); }; /** * 返り値の message のテスト * @param message * @param id * @param timestamp * @param rfc822msgId */ const assertMessage = (message, id, timestamp, rfc822msgId) => { expect(message.id).toEqual(id); expect(message.timestamp).toEqual(timestamp); expect(message.rfc822msgId).toEqual(rfc822msgId); }; const DATETIME_FORMAT = 'yyyy-MM-dd\'T\'HH:mm:ssX'; const LABEL_LIST_RES = { "labels": [ { "id": "CHAT", "name": "CHAT", "messageListVisibility": "hide", "labelListVisibility": "labelHide", "type": "system" }, { "id": "SENT", "name": "SENT", "type": "system" }, { "id": "INBOX", "name": "INBOX", "type": "system" }, { "id": "IMPORTANT", "name": "IMPORTANT", "messageListVisibility": "hide", "labelListVisibility": "labelHide", "type": "system" }, { "id": "TRASH", "name": "TRASH", "messageListVisibility": "hide", "labelListVisibility": "labelHide", "type": "system" }, { "id": "DRAFT", "name": "DRAFT", "type": "system" }, { "id": "SPAM", "name": "SPAM", "messageListVisibility": "hide", "labelListVisibility": "labelHide", "type": "system" }, { "id": "CATEGORY_FORUMS", "name": "CATEGORY_FORUMS", "messageListVisibility": "hide", "labelListVisibility": "labelHide", "type": "system" }, { "id": "CATEGORY_UPDATES", "name": "CATEGORY_UPDATES", "messageListVisibility": "hide", "labelListVisibility": "labelHide", "type": "system" }, { "id": "CATEGORY_PERSONAL", "name": "CATEGORY_PERSONAL", "messageListVisibility": "hide", "labelListVisibility": "labelHide", "type": "system" }, { "id": "CATEGORY_PROMOTIONS", "name": "CATEGORY_PROMOTIONS", "messageListVisibility": "hide", "labelListVisibility": "labelHide", "type": "system" }, { "id": "CATEGORY_SOCIAL", "name": "CATEGORY_SOCIAL", "messageListVisibility": "hide", "labelListVisibility": "labelHide", "type": "system" }, { "id": "STARRED", "name": "STARRED", "type": "system" }, { "id": "UNREAD", "name": "UNREAD", "type": "system" }, { "id": "Label_1234567", "name": "ラベル 1", "type": "user" }, { "id": "Label_2345678", "name": "ラベル 2", "type": "user" }, { "id": "Label_3456789", "name": "ラベル 3", "type": "user" } ] }; const MESSAGE_METADATA_1 = { "id": "abc123", "payload": { "headers": [ { "name": "From", "value": "from@example.com" }, { "name": "To", "value": "to@example.com" }, { "name": "Message-ID", "value": "rfc822msgId-1234567" }, { "name": "Subject", "value": "メール 1 の件名" } ] }, "internalDate": "1661482800000" // 2022-08-26T12:00:00+09:00 }; const MESSAGE_METADATA_2 = { "id": "def456", "payload": { "headers": [ { "name": "From", "value": "from@example.com" }, { "name": "To", "value": "to@example.com" }, { "name": "Message-ID", "value": "rfc822msgId-4567890" }, { "name": "Subject", "value": "メール 2 の件名" } ] }, "internalDate": "1660100400000" // 2022-08-10T12:00:00+09:00 }; const MESSAGE_METADATA_3 = { "id": "efg789", "payload": { "headers": [ { "name": "From", "value": "from@example.com" }, { "name": "To", "value": "to@example.com" }, { "name": "Message-ID", "value": "rfc822msgId-7890123" }, { "name": "Subject", "value": "メール 3 の件名" } ] }, "internalDate": "1659322800000" // 2022-08-01T12:00:00+09:00 }; const SAMPLE_MESSAGE_LIST_RES = { "messages": [ { "id": "abc123" }, { "id": "def456" }, { "id": "efg789" } ] }; const MESSAGE_DETAILS = [ MESSAGE_METADATA_1, MESSAGE_METADATA_2, MESSAGE_METADATA_3 ]; /** * 正常系のテストケース * ラベルを 1 つだけ指定した場合 */ test('Success - one label', () => { const label = 'ラベル 1'; const labelIds = ['Label_1234567']; prepareConfigs(label); const limit = 3; const timestampLowerLimit = dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T00:00:00+09:00'); let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertGetLabelListRequest(request); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(LABEL_LIST_RES)); } if (reqCount === 1) { assertGetMessageListRequest(request, labelIds, limit, timestampLowerLimit); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_MESSAGE_LIST_RES)); } // message ごとにリクエスト const messageCount = reqCount-2; assertGetMessageRequest(request, SAMPLE_MESSAGE_LIST_RES.messages[messageCount].id); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(MESSAGE_DETAILS[messageCount])); }); const result = list(limit, timestampLowerLimit); expect(result.length).toEqual(3); // 一覧取得のままの順序で返る assertMessage(result[0], 'abc123', dateFormatter.parse(DATETIME_FORMAT, '2022-08-26T12:00:00+09:00'), 'rfc822msgId-1234567' ); assertMessage(result[1], 'def456', dateFormatter.parse(DATETIME_FORMAT, '2022-08-10T12:00:00+09:00'), 'rfc822msgId-4567890' ); assertMessage(result[2], 'efg789', dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T12:00:00+09:00'), 'rfc822msgId-7890123' ); }); /** * 正常系のテストケース * ラベルを指定しなかった場合 */ test('Success - no label', () => { prepareConfigs(''); const limit = 2; const timestampLowerLimit = dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T00:00:00+09:00'); const messageListRes = { "messages": [ // limit が 2 の場合、2件までしか返らない { "id": "abc123" }, { "id": "def456" } ] }; const messageDetails = [ MESSAGE_METADATA_1, MESSAGE_METADATA_2 ]; let reqCount = 0; httpClient.setRequestHandler((request) => { // ラベル一覧取得のリクエストはなし。 if (reqCount === 0) { assertGetMessageListRequest(request, ['INBOX'], limit, timestampLowerLimit); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(messageListRes)); } // message ごとにリクエスト const messageCount = reqCount-1; assertGetMessageRequest(request, messageListRes.messages[messageCount].id); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(messageDetails[messageCount])); }); const result = list(limit, timestampLowerLimit); expect(result.length).toEqual(2); // 一覧取得のままの順序で返る assertMessage(result[0], 'abc123', dateFormatter.parse(DATETIME_FORMAT, '2022-08-26T12:00:00+09:00'), 'rfc822msgId-1234567' ); assertMessage(result[1], 'def456', dateFormatter.parse(DATETIME_FORMAT, '2022-08-10T12:00:00+09:00'), 'rfc822msgId-4567890' ); }); /** * 正常系のテストケース * ラベルを複数件指定した場合 */ test('Success - more than one labels', () => { // ラベルを改行区切りで複数件、空行や前後に空白をふくめて指定する const label = `ラベル 3 INBOX STARRED ラベル 2 ラベル 1`; const labelIds = [ // LABEL_LIST_RES に含まれる順になる 'INBOX', 'STARRED', 'Label_1234567', 'Label_2345678', 'Label_3456789' ]; prepareConfigs(label); const limit = 3; const timestampLowerLimit = dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T00:00:00+09:00'); let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertGetLabelListRequest(request); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(LABEL_LIST_RES)); } if (reqCount === 1) { assertGetMessageListRequest(request, labelIds, limit, timestampLowerLimit); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_MESSAGE_LIST_RES)); } // message ごとにリクエスト const messageCount = reqCount-2; assertGetMessageRequest(request, SAMPLE_MESSAGE_LIST_RES.messages[messageCount].id); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(MESSAGE_DETAILS[messageCount])); }); const result = list(limit, timestampLowerLimit); expect(result.length).toEqual(3); // 一覧取得のままの順序で返る assertMessage(result[0], 'abc123', dateFormatter.parse(DATETIME_FORMAT, '2022-08-26T12:00:00+09:00'), 'rfc822msgId-1234567' ); assertMessage(result[1], 'def456', dateFormatter.parse(DATETIME_FORMAT, '2022-08-10T12:00:00+09:00'), 'rfc822msgId-4567890' ); assertMessage(result[2], 'efg789', dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T12:00:00+09:00'), 'rfc822msgId-7890123' ); }); /** * 正常系のテストケース * プロセス開始済みのメールは一覧から除かれる */ test('Success - filtered by isProcessStarted', () => { const label = 'ラベル 1'; const labelIds = ['Label_1234567']; prepareConfigs(label); const limit = 3; const timestampLowerLimit = dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T00:00:00+09:00'); engine.addProcessStartedSet('def456'); // 2 件目のメールをプロセス開始済みにセット let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertGetLabelListRequest(request); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(LABEL_LIST_RES)); } if (reqCount === 1) { assertGetMessageListRequest(request, labelIds, limit, timestampLowerLimit); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_MESSAGE_LIST_RES)); } // 1 件目、3件目のメールのみ詳細取得のリクエストが送付される if (reqCount === 2) { assertGetMessageRequest(request, SAMPLE_MESSAGE_LIST_RES.messages[0].id); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(MESSAGE_DETAILS[0])); } assertGetMessageRequest(request, SAMPLE_MESSAGE_LIST_RES.messages[2].id); return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(MESSAGE_DETAILS[2])); }); const result = list(limit, timestampLowerLimit); expect(result.length).toEqual(2); // 2件のみ // 一覧取得のままの順序で返る assertMessage(result[0], 'abc123', dateFormatter.parse(DATETIME_FORMAT, '2022-08-26T12:00:00+09:00'), 'rfc822msgId-1234567' ); assertMessage(result[1], 'efg789', dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T12:00:00+09:00'), 'rfc822msgId-7890123' ); }); /** * 正常系のテストケース * timestampLowerLimit より前のメールは一覧から除かれる */ test('Success - filtered by timestampLowerLimit', () => { const label = 'ラベル 1'; const labelIds = ['Label_1234567']; prepareConfigs(label); const limit = 3; // 1 件目のメールのみプロセス開始対象となるように timestampLowerLimit を設定 const timestampLowerLimit = dateFormatter.parse(DATETIME_FORMAT, '2022-08-10T12:00:01+09:00'); let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertGetLabelListRequest(request); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(LABEL_LIST_RES)); } if (reqCount === 1) { assertGetMessageListRequest(request, labelIds, limit, timestampLowerLimit); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_MESSAGE_LIST_RES)); } // message ごとにリクエスト const messageCount = reqCount-2; assertGetMessageRequest(request, SAMPLE_MESSAGE_LIST_RES.messages[messageCount].id); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(MESSAGE_DETAILS[messageCount])); }); const result = list(limit, timestampLowerLimit); expect(result.length).toEqual(1); // 1件のみ assertMessage(result[0], 'abc123', dateFormatter.parse(DATETIME_FORMAT, '2022-08-26T12:00:00+09:00'), 'rfc822msgId-1234567' ); }); /** * 異常系のテストケース * ラベル一覧を取得する GET リクエストで 403 エラー */ test('Fail - 403 error in getting labels', () => { prepareConfigs('ラベル 1'); const limit = 3; const timestampLowerLimit = dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T00:00:00+09:00'); httpClient.setRequestHandler((request) => { assertGetLabelListRequest(request); return httpClient.createHttpResponse(403, 'application/json', JSON.stringify({})); }); try { list(limit, timestampLowerLimit); fail(); } catch (e) { expect(e.toString()).toEqual('Failed to get labels. status: 403'); } }); /** * 異常系のテストケース * 存在しないラベルを指定した場合 */ test('Fail - label does not exist', () => { const label = `IMPORTANT 存在しないラベル 1 存在しないラベル 2`; prepareConfigs(label); const limit = 3; const timestampLowerLimit = dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T00:00:00+09:00'); httpClient.setRequestHandler((request) => { assertGetLabelListRequest(request); return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(LABEL_LIST_RES)); }); try { list(limit, timestampLowerLimit); fail(); } catch (e) { expect(e.toString()).toEqual('label ids of 存在しないラベル 1, 存在しないラベル 2 not found'); } }); /** * 異常系のテストケース * メッセージ一覧を取得する GET リクエストで 403 エラー */ test('Fail - 403 error in getting messages', () => { const label = 'ラベル 1'; const labelIds = ['Label_1234567']; prepareConfigs(label); const limit = 3; const timestampLowerLimit = dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T00:00:00+09:00'); let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertGetLabelListRequest(request); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(LABEL_LIST_RES)); } assertGetMessageListRequest(request, labelIds, limit, timestampLowerLimit); return httpClient.createHttpResponse(403, 'application/json', JSON.stringify({})); }); try { list(limit, timestampLowerLimit); fail(); } catch (e) { expect(e.toString()).toEqual('Failed to get messages. status: 403'); } }); /** * 異常系のテストケース * メッセージの詳細を取得する GET リクエストで 403 エラー */ test('Fail - 403 error in getting a message', () => { const label = 'ラベル 1'; const labelIds = ['Label_1234567']; prepareConfigs(label); const limit = 3; const timestampLowerLimit = dateFormatter.parse(DATETIME_FORMAT, '2022-08-01T00:00:00+09:00'); let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertGetLabelListRequest(request); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(LABEL_LIST_RES)); } if (reqCount === 1) { assertGetMessageListRequest(request, labelIds, limit, timestampLowerLimit); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(SAMPLE_MESSAGE_LIST_RES)); } if (reqCount === 2) { // 1 つ目のメッセージ詳細を取得 assertGetMessageRequest(request, SAMPLE_MESSAGE_LIST_RES.messages[0].id); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(MESSAGE_METADATA_1)); } // 2 つ目のメッセージ詳細の取得で失敗 assertGetMessageRequest(request, SAMPLE_MESSAGE_LIST_RES.messages[1].id); return httpClient.createHttpResponse(403, 'application/json', JSON.stringify({})); }); try { list(limit, timestampLowerLimit); fail(); } catch (e) { expect(e.toString()).toEqual('Failed to get message def456. status: 403'); } }); ]]>