3 2 2023-07-03 (C) Questetra, Inc. (MIT License) This item adds or updates a contact on SendGrid with the specified custom field values. If a contact with the specified email address already exists, the contact will be updated. この工程は、SendGrid の宛先を追加または更新し、指定したカスタムフィールドに値を設定します。指定したメールアドレスの宛先がすでにある場合、その宛先が更新されます。 https://support.questetra.com/bpmn-icons/service-task-sendgrid-contact-upsert-custom-fields/ https://support.questetra.com/ja/bpmn-icons/service-task-sendgrid-contact-upsert-custom-fields/ iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADBUlEQVRYR8WXS08TURTH/5cWBeXZ ipHFjBh3JIYSdKELYePGZCYS4jMxtiauwU+gJm40JpYPYKhxodEoj+4R4lYrjQsXimKrUhKRNxNC 22vuTMcZ5s50psLgbCY5c++c3zn33PMg+M8PqUh/MnMOFN0gJAKgCQB7s2cKwCIoZe8JyOKo1/+6 AwwvNCG42g9KB0AIU+r+ULoIQuLI1w2it3mx3IbyAGPfo0DxoWfFVk0qCGKQxBEnCGeAZDYOoN/d XA8rKI1DFm/arbQHSGYSALnm4dfel1CagCzGrBt4ABfL+w7V4lhDNae4LkiwmqecPKsU8Ci7pssH IQkD5kVbAdiZEzpUzqzJUy04HdrLLfm4mse9z8uc/OzBWlxMzZvktNccEwYAi/bAyle3gHMCSC1t ouvNHAfwpDOEq+9/G3IWmIX6I/rtMACS2dsAbrkd6rYBNAV3IAlMHwyAsQy7u427AsC8IIvNBgDL cCDDbsrZ9x3yAAAtFjQP2ET+DXE/zrTUcEztddVorani5OO/NvBNySNICIIECBCCKgJMr+Vxf3rF zjb1RmgAY9kJEHSbVz3vCuN8ay238ct6AUfHZzn565Mt6Anzt2Mkp6D3rfkWlLZSTEIWenQPzAA4 7AfAq5yCPjsAVsAkoVMH4DLITnngxayCC+9sPMCslQTiO8Czn+u4nDLlAbObTQC+HcHTH+u4Yk5E BkAakhBxDMKXx8OINOzhgq1IKeY2ipz8UmoeoycOcPLhnIK7n/gUDUsQcqV3qCOEWJp3XaV5oExu MV1Dm0TkP4A5Eam5YGsq9hWA0iXIotreORYjXwFsi5FWjmf0guQbALO+UN/Gl2P1GIyG5EF7I8LV AS6G2vYFwLof6/NheRPX0wse6plTQ6Jv3clmlMf52wfon3avKQV9DEmMWpl2py03BZ13ALVPUEex hJdOyfbwWcARRP9tMNH/qI1mA6XRzLVlU7epitXRLL690cxqlpYxe0ARAVGH047SkjQo2Bg2BdCJ chZXdgQeLtV2l/wBk2V1MIOcaTsAAAAASUVORK5CYII= } fieldConfigMap */ const prepareConfigs = (email, fieldConfigMap) => { configs.put('conf_Auth', 'SendGrid'); setDataItem('Email', 10, 'STRING_TEXTFIELD', email); fieldConfigMap.forEach((fieldConfig, i) => { configs.put(`conf_FieldName${i}`, fieldConfig.name); setDataItem(`FieldValue${i}`, i, fieldConfig.type, fieldConfig.value); }); }; /** * データ項目を作成し、config にセットする * @param name config 名の conf_ 以降 * @param index * @param type * @param value */ const setDataItem = (name, index, type, value) => { if (type === null) { return; } const dataDef = engine.createDataDefinition(name, index, `q_${name}`, type); configs.putObject(`conf_${name}`, dataDef); if (value === null) { return; } switch (type) { case 'STRING_TEXTFIELD': engine.setData(dataDef, value); break; case 'DECIMAL': engine.setData(dataDef, java.math.BigDecimal.valueOf(value)); break; case 'DATE_YMD': engine.setData(dataDef, java.sql.Date.valueOf(value)); break; } }; /** * 異常系のテスト * @param func * @param errorMsg */ const assertError = (func, errorMsg) => { try { func(); fail(); } catch (e) { expect(e.toString()).toEqual(errorMsg); } }; /** * 指定の長さの文字列を作成 * @param length * @return string */ const createString = (length) => { const sourceStr = 'abcdefghijklmnopqrstuvwxyz'; const string = sourceStr.repeat(Math.floor(length / sourceStr.length)) + sourceStr.slice(0, length % sourceStr.length); return string; } /** * メールアドレスが空 */ test('Email Address is blank', () => { prepareConfigs(null, new Map()); assertError(main, `Email Address is blank.`); }); /** * メールアドレスが長すぎる */ test('Email Address is too long', () => { const email = createString(MAX_EMAIL_LENGTH + 1); prepareConfigs(email, new Map()); assertError(main, `Email Address must be within ${MAX_EMAIL_LENGTH} characters.`); }); /** * 宛先を追加/更新する API リクエストのテスト * @param {Object} request * @param request.url * @param request.method * @param request.contentType * @param request.body * @param contact */ const assertUpsertRequest = ({url, method, contentType, body}, contact) => { expect(url).toEqual('https://api.sendgrid.com/v3/marketing/contacts'); expect(method).toEqual('PUT'); expect(contentType).toEqual('application/json'); const bodyObj = JSON.parse(body); expect(bodyObj.contacts[0]).toEqual(contact); }; /** * 宛先を追加/更新する API リクエストでエラー */ test('Fail to upsert contact', () => { const email = createString(MAX_EMAIL_LENGTH); prepareConfigs(email, new Map()); // カスタムフィールドの指定なし httpClient.setRequestHandler((request) => { assertUpsertRequest(request, {email}); return httpClient.createHttpResponse(400, 'application/json', '{}'); }); assertError(main, 'Failed to upsert contact. status: 400'); }); /** * ジョブの処理状況を確認する API リクエストのテスト * @param {Object} request * @param request.url * @param request.method * @param jobId */ const assertCheckStatusRequest = ({url, method}, jobId) => { expect(url).toEqual(`https://api.sendgrid.com/v3/marketing/contacts/imports/${jobId}`); expect(method).toEqual('GET'); }; /** * ジョブの処理状況を確認する API リクエストでエラー */ test('Fail to get upsert status', () => { const email = createString(MAX_EMAIL_LENGTH); prepareConfigs(email, new Map()); // カスタムフィールドの指定なし let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertUpsertRequest(request, {email}); reqCount++; return httpClient.createHttpResponse(202, 'application/json', '{"job_id": "job1"}'); } assertCheckStatusRequest(request, 'job1'); return httpClient.createHttpResponse(400, 'application/json', '{}'); }); assertError(main, 'Failed to get upsert status. status: 400'); }); /** * 1 回目のステータス確認で、ジョブが異常終了 */ test('Fail to complete upserting in main()', () => { const email = createString(MAX_EMAIL_LENGTH); prepareConfigs(email, new Map()); // カスタムフィールドの指定なし let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertUpsertRequest(request, {email}); reqCount++; return httpClient.createHttpResponse(202, 'application/json', '{"job_id": "job1"}'); } assertCheckStatusRequest(request, 'job1'); return httpClient.createHttpResponse(200, 'application/json', '{"status": "failed"}'); }); assertError(main, 'Failed to complete upserting the contact.'); }); /** * 成功 * カスタムフィールドを指定しない場合 */ test('Succeed - No Custom Fields', () => { const email = createString(MAX_EMAIL_LENGTH); prepareConfigs(email, new Map()); // カスタムフィールドの指定なし let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertUpsertRequest(request, {email}); reqCount++; return httpClient.createHttpResponse(202, 'application/json', '{"job_id": "job1"}'); } assertCheckStatusRequest(request, 'job1'); return httpClient.createHttpResponse(200, 'application/json', '{"status": "completed"}'); }); expect(main()).toEqual(undefined); }); /** * フィールド名が空なのに、値のデータ項目が選択されている */ test('Field Name is blank while its value is selected', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); fieldConfigMap.set(3, {name: '', type: 'STRING_TEXTFIELD', value: 'フィールドの値'}); prepareConfigs(email, fieldConfigMap); assertError(main, `Custom Field Name 3 is blank while its value is selected.`); }); /** * フィールド名の指定が重複 */ test('Same Field Name is set multiple times', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); fieldConfigMap.set(1, {name: 'field_1', type: 'STRING_TEXTFIELD', value: 'フィールドの値'}); fieldConfigMap.set(3, {name: 'field_1', type: 'STRING_TEXTFIELD', value: 'フィールドの値'}); prepareConfigs(email, fieldConfigMap); assertError(main, `Custom Field Name "field_1" is set multiple times.`); }); /** * フィールド名は設定されているのに、値のデータ項目が選択されていない */ test('Field Value is not selected while its field name is set', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); fieldConfigMap.set(4, {name: 'field_4', type: null, value: null}); prepareConfigs(email, fieldConfigMap); assertError(main, `Value 4 is not selected while its field name is set.`); }); /** * フィールドの値が文字列で、長すぎる */ test('Field Value is too long', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); const fieldValue = createString(MAX_FIELD_VALUE_LENGTH + 1); fieldConfigMap.set(1, {name: 'field_1', type: 'STRING_TEXTFIELD', value: fieldValue}); prepareConfigs(email, fieldConfigMap); assertError(main, `Custom Field Value of "field_1" must be within ${MAX_FIELD_VALUE_LENGTH} characters.`); }); /** * 全カスタムフィールドの一覧を取得する API リクエストのテスト * @param {Object} request * @param request.url * @param request.method */ const assertGetAllCustomFieldsRequest = ({url, method}) => { expect(url).toEqual('https://api.sendgrid.com/v3/marketing/field_definitions'); expect(method).toEqual('GET'); }; /** * 全カスタムフィールドの一覧を取得する API リクエストでエラー */ test('Fail to get all custom fields', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); fieldConfigMap.set(1, {name: 'field_1', type: 'STRING_TEXTFIELD', value: 'フィールドの値'}); prepareConfigs(email, fieldConfigMap); httpClient.setRequestHandler((request) => { assertGetAllCustomFieldsRequest(request); return httpClient.createHttpResponse(400, 'application/json', '{}'); }); assertError(main, 'Failed to get field definitions. status: 400'); }); const ALL_CUSTOM_FIELDS_RESPONSE = { custom_fields: [ { id: '1T', name: 'field_1', field_type: 'Text' }, { id: '2T', name: 'field_2', field_type: 'Text' }, { id: '3T', name: 'field_3', field_type: 'Text' }, { id: '4T', name: 'field_4', field_type: 'Text' }, { id: '5T', name: 'field_5', field_type: 'Text' }, { id: '6T', name: 'field_6', field_type: 'Text' }, { id: '7T', name: 'field_7', field_type: 'Text' }, { id: '8T', name: 'field_8', field_type: 'Text' }, { id: '9N', name: 'field_9', field_type: 'Number' }, { id: '10D', name: 'field_10', field_type: 'Date' } ] }; /** * 存在しないカスタムフィールド名を指定 */ test('Custom Field does not exist', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); fieldConfigMap.set(1, {name: 'field_100', type: 'STRING_TEXTFIELD', value: 'フィールドの値'}); prepareConfigs(email, fieldConfigMap); httpClient.setRequestHandler((request) => { assertGetAllCustomFieldsRequest(request); return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(ALL_CUSTOM_FIELDS_RESPONSE)); }); assertError(main, 'Custom Field named "field_100" does not exist.'); }); /** * 文字型のカスタムフィールドの値を、文字型以外で指定 */ test('Custom Field is Text type but its value is not set by STRING_TEXTFIELD type data item', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); fieldConfigMap.set(1, {name: 'field_1', type: 'DECIMAL', value: 123}); prepareConfigs(email, fieldConfigMap); httpClient.setRequestHandler((request) => { assertGetAllCustomFieldsRequest(request); return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(ALL_CUSTOM_FIELDS_RESPONSE)); }); assertError(main, 'Custom Field "field_1" is Text type. Its value must be set by STRING_TEXTFIELD type data item.'); }); /** * 数値型のカスタムフィールドの値を、数値型以外で指定 */ test('Custom Field is Number type but its value is not set by DECIMAL type data item', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); fieldConfigMap.set(2, {name: 'field_9', type: 'STRING_TEXTFIELD', value: 'フィールドの値'}); prepareConfigs(email, fieldConfigMap); httpClient.setRequestHandler((request) => { assertGetAllCustomFieldsRequest(request); return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(ALL_CUSTOM_FIELDS_RESPONSE)); }); assertError(main, 'Custom Field "field_9" is Number type. Its value must be set by DECIMAL type data item.'); }); /** * 日付型のカスタムフィールドの値を、日付型以外で指定 */ test('Custom Field is Date type but its value is not set by DATE_YMD type data item', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); fieldConfigMap.set(3, {name: 'field_10', type: 'STRING_TEXTFIELD', value: 'フィールドの値'}); prepareConfigs(email, fieldConfigMap); httpClient.setRequestHandler((request) => { assertGetAllCustomFieldsRequest(request); return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(ALL_CUSTOM_FIELDS_RESPONSE)); }); assertError(main, 'Custom Field "field_10" is Date type. Its value must be set by DATE_YMD type data item.'); }); /** * 成功 * カスタムフィールドを最大個数、最長文字数の文字列で指定 */ test('Succeed - Maximum number of Custom Fields', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); const customFields = {}; // リクエストボディのアサート用オブジェクト const fieldValuePrefix = createString(MAX_FIELD_VALUE_LENGTH - 2); for (let i = 0; i < FIELD_NUM; i++) { const fieldName = `field_${i + 1}`; const fieldId = `${i + 1}T`; const fieldValue = `${fieldValuePrefix}_${i + 1}`; fieldConfigMap.set(i + 1, {name: fieldName, type: 'STRING_TEXTFIELD', value: fieldValue}); Object.assign(customFields, {[fieldId]: fieldValue}); } prepareConfigs(email, fieldConfigMap); const contact = { email, custom_fields: customFields }; let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertGetAllCustomFieldsRequest(request); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(ALL_CUSTOM_FIELDS_RESPONSE)); } if (reqCount === 1) { assertUpsertRequest(request, contact); reqCount++; return httpClient.createHttpResponse(202, 'application/json', '{"job_id": "job2"}'); } assertCheckStatusRequest(request, 'job2'); return httpClient.createHttpResponse(200, 'application/json', '{"status": "completed"}'); }); expect(main()).toEqual(undefined); }); /** * 成功 * 文字型、数値型、日付型のカスタムフィールドが混在 */ test('Succeed - All types of Custom Fields', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); fieldConfigMap.set(1, {name: 'field_1', type: 'STRING_TEXTFIELD', value: 'フィールドの値'}); fieldConfigMap.set(4, {name: 'field_9', type: 'DECIMAL', value: 123}); fieldConfigMap.set(7, {name: 'field_10', type: 'DATE_YMD', value: '2021-01-31'}); prepareConfigs(email, fieldConfigMap); const customFields = { '1T': 'フィールドの値', '9N': 123, '10D': '01/31/2021' }; const contact = { email, custom_fields: customFields }; let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertGetAllCustomFieldsRequest(request); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(ALL_CUSTOM_FIELDS_RESPONSE)); } if (reqCount === 1) { assertUpsertRequest(request, contact); reqCount++; return httpClient.createHttpResponse(202, 'application/json', '{"job_id": "job3"}'); } assertCheckStatusRequest(request, 'job3'); return httpClient.createHttpResponse(200, 'application/json', '{"status": "completed"}'); }); expect(main()).toEqual(undefined); }); /** * 成功 * フィールドの値が null の場合、空文字列が送られる */ test('Succeed - Custom Data Value is null', () => { const email = createString(MAX_EMAIL_LENGTH); const fieldConfigMap = new Map(); fieldConfigMap.set(1, {name: 'field_1', type: 'STRING_TEXTFIELD', value: null}); fieldConfigMap.set(4, {name: 'field_9', type: 'DECIMAL', value: null}); fieldConfigMap.set(7, {name: 'field_10', type: 'DATE_YMD', value: null}); fieldConfigMap.set(8, {name: 'field_2', type: 'STRING_TEXTFIELD', value: 'フィールドの値'}); prepareConfigs(email, fieldConfigMap); const customFields = { '1T': '', '9N': '', '10D': '', '2T': 'フィールドの値' }; const contact = { email, custom_fields: customFields }; let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertGetAllCustomFieldsRequest(request); reqCount++; return httpClient.createHttpResponse(200, 'application/json', JSON.stringify(ALL_CUSTOM_FIELDS_RESPONSE)); } if (reqCount === 1) { assertUpsertRequest(request, contact); reqCount++; return httpClient.createHttpResponse(202, 'application/json', '{"job_id": "job4"}'); } assertCheckStatusRequest(request, 'job4'); return httpClient.createHttpResponse(200, 'application/json', '{"status": "completed"}'); }); expect(main()).toEqual(undefined); }); /** * 2 回目のステータス確認で、ジョブが異常終了 */ test('Fail to complete upserting in proceed()', () => { const email = createString(MAX_EMAIL_LENGTH); prepareConfigs(email, new Map()); // カスタムフィールドの指定なし let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertUpsertRequest(request, {email}); reqCount++; return httpClient.createHttpResponse(202, 'application/json', '{"job_id": "job5"}'); } assertCheckStatusRequest(request, 'job5'); return httpClient.createHttpResponse(200, 'application/json', '{"status": "pending"}'); }); expect(main()).toEqual(false); expect(engine.restoreTemporaryData()).toEqual('job5'); httpClient.setRequestHandler((request) => { assertCheckStatusRequest(request, 'job5'); return httpClient.createHttpResponse(200, 'application/json', '{"status": "errored"}'); }); assertError(proceed, 'Failed to complete upserting the contact.'); }); /** * 成功 * proceed() のテスト * 3 回目のステータス確認で、ジョブが完了 */ test('Succeed - Complete in 3rd request', () => { const email = createString(MAX_EMAIL_LENGTH); prepareConfigs(email, new Map()); // カスタムフィールドの指定なし let reqCount = 0; httpClient.setRequestHandler((request) => { if (reqCount === 0) { assertUpsertRequest(request, {email}); reqCount++; return httpClient.createHttpResponse(202, 'application/json', '{"job_id": "job6"}'); } assertCheckStatusRequest(request, 'job6'); return httpClient.createHttpResponse(200, 'application/json', '{"status": "pending"}'); }); expect(main()).toEqual(false); expect(engine.restoreTemporaryData()).toEqual('job6'); httpClient.setRequestHandler((request) => { assertCheckStatusRequest(request, 'job6'); return httpClient.createHttpResponse(200, 'application/json', '{"status": "pending"}'); }); expect(proceed()).toEqual(false); httpClient.setRequestHandler((request) => { assertCheckStatusRequest(request, 'job6'); return httpClient.createHttpResponse(200, 'application/json', '{"status": "completed"}'); }); expect(proceed()).toEqual(undefined); }); ]]>