Twilio SendGrid: 宛先追加/更新(カスタムフィールド)

Twilio SendGrid: Add or Update Contact (Custom Fields)

この工程は、SendGrid の宛先を追加または更新し、指定したカスタムフィールドに値を設定します。指定したメールアドレスの宛先がすでにある場合、その宛先が更新されます。

Basic Configs
工程名
メモ
Auto Step icon
Configs for this Auto Step
conf_Auth
C1: API キーをトークンとして設定した認証設定 *
conf_Email
C2: メールアドレス *
conf_FieldName1
C3F: カスタムフィールド名_1
conf_FieldValue1
C3V: 値_1
conf_FieldName2
C4F: カスタムフィールド名_2
conf_FieldValue2
C4V: 値_2
conf_FieldName3
C5F: カスタムフィールド名_3
conf_FieldValue3
C5V: 値_3
conf_FieldName4
C6F: カスタムフィールド名_4
conf_FieldValue4
C6V: 値_4
conf_FieldName5
C7F: カスタムフィールド名_5
conf_FieldValue5
C7V: 値_5
conf_FieldName6
C8F: カスタムフィールド名_6
conf_FieldValue6
C8V: 値_6
conf_FieldName7
C9F: カスタムフィールド名_7
conf_FieldValue7
C9V: 値_7
conf_FieldName8
C10F: カスタムフィールド名_8
conf_FieldValue8
C10V: 値_8

Notes

  • カスタムフィールドの使用方法については、SendGrid のドキュメントを参照してください
    • カスタムフィールドの値として指定するデータ項目の型は、カスタムフィールドの型と一致させる必要があります
カスタムフィールドの型データ項目の型
Text文字 (単一行)
Number数値
Date日付 (年月日)

Capture

See Also

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


const FIELD_NUM = 8; // 扱えるフィールドの数
const MAX_EMAIL_LENGTH = 254;
const MAX_FIELD_VALUE_LENGTH = 1000;

function main(){
    //// == Config Retrieving / 工程コンフィグの参照 ==
    const auth = configs.get('conf_Auth');
    const email = retrieveEmail();
    const customFieldMap = retrieveCustomFieldsAsMap();

    //// == Calculating / 演算 ==
    const contact = {email};
    if (customFieldMap.size > 0) {
        const allCustomFields = getAllCustomFields(auth);
        setCustomFields(contact, customFieldMap, allCustomFields);
    }
    const jobId = upsertContact(auth, contact);

    // 処理状況を確認し、未完了なら proceed() に進む
    if (checkStatus(auth, jobId) === false) {
        engine.saveTemporaryData(jobId);
        return false;
    }
}

function proceed() {
    //// == Config Retrieving / 工程コンフィグの参照 ==
    const auth = configs.get('conf_Auth');

    //// == Restoring Temporary Data / 一時データの読み出し ==
    const jobId = engine.restoreTemporaryData();
    if (jobId === null) {
        throw 'Temporary data has not been saved.';
    }

    //// == 演算 / Calculating ==
    if (checkStatus(auth, jobId) === false) {
        return false;
    }
}

/**
  * config に設定されたメールアドレスを読み出す
  * @return {String} email
  */
function retrieveEmail() {
    const emailDef = configs.getObject('conf_Email');
    const email = engine.findData(emailDef);
    if (email === null) {
        throw 'Email Address is blank.';
    }
    if (email.length > MAX_EMAIL_LENGTH) {
        throw `Email Address must be within ${MAX_EMAIL_LENGTH} characters.`;
    }
    return email;
}

/**
  * config に設定されたカスタムフィールドの情報を読み出す
  * Map のキーはフィールド名、値はデータ定義
  * @return {Map<String, DataDefinitionView>} customFieldMap
  */
function retrieveCustomFieldsAsMap() {
    const customFieldMap = new Map();
    for (let i = 0; i < FIELD_NUM; i++) {
        const fieldName = configs.get(`conf_FieldName${i + 1}`);
        const fieldValueDef = configs.getObject(`conf_FieldValue${i + 1}`);
        if (fieldName === '' || fieldName === null) {
            if (fieldValueDef !== null) { // フィールド名が空なのに、値のデータ項目が選択されている場合
                throw `Custom Field Name ${i + 1} is blank while its value is selected.`;
            }
            continue;
        }
        if (customFieldMap.has(fieldName)) { // フィールド名の指定が重複
            throw `Custom Field Name "${fieldName}" is set multiple times.`;
        }
        if (fieldValueDef === null) { // フィールド名は設定されているのに、値のデータ項目が選択されてない場合
            // 「SendGrid: 宛先追加/更新」と仕様を合わせるため、null をセットせずにエラーにする
            throw `Value ${i + 1} is not selected while its field name is set.`;
        }
        const dataObj = engine.findData(fieldValueDef);
        // 文字型データを想定した文字数チェック。数値型、日付型データ以外が文字数制限に引っかかることはありえない
        if (dataObj !== null && dataObj.toString().length > MAX_FIELD_VALUE_LENGTH) {
            throw `Custom Field Value of "${fieldName}" must be within ${MAX_FIELD_VALUE_LENGTH} characters.`;
        }
        customFieldMap.set(fieldName, fieldValueDef);
    }
    return customFieldMap;
}

/**
  * 全カスタムフィールドの一覧を取得する
  * @param {String} auth 認証設定名
  * @return {Array<Object>} allCustomFields
  */
function getAllCustomFields(auth) {
    const response = httpClient.begin()
        .authSetting(auth)
        .get('https://api.sendgrid.com/v3/marketing/field_definitions');

    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) {
        engine.log(responseStr);
        throw `Failed to get field definitions. status: ${status}`;
    }
    return JSON.parse(responseStr).custom_fields;
}

/**
  * 宛先オブジェクトにカスタムフィールドの情報を追加する
  * @param {Object} contact 宛先オブジェクト
  * @param {Map<String, DataDefinitionView>} customFieldMap カスタムフィールドの Map
  * @param {Array<Object>} allCustomFields 全カスタムフィールドの一覧
  */
function setCustomFields(contact, customFieldMap, allCustomFields) {
    const customFields = {};
    customFieldMap.forEach((dataDef, fieldName) => {
        const customField = allCustomFields.find(field => field.name === fieldName);
        if (customField === undefined) { // 指定したフィールド名のカスタムフィールドがない場合
            throw `Custom Field named "${fieldName}" does not exist.`;
        }
        switch (customField.field_type) {
            case 'Text':
                addTextField(customFields, customField.id, customField.name, dataDef);
                break;
            case 'Number':
                addNumberField(customFields, customField.id, customField.name, dataDef);
                break;
            case 'Date':
                addDateField(customFields, customField.id, customField.name, dataDef);
                break;
            default:
                // ありえないが、念のため
                throw `Custom Field type "${customField.field_type}" is not supported.`;
        }
    });
    Object.assign(contact, {custom_fields: customFields});
}

/**
  * 文字型カスタムフィールドの情報を追加する
  * @param {Object} customFields 設定するカスタムフィールド一覧
  * @param {String} customFieldId カスタムフィールドの ID
  * @param {String} customFieldName カスタムフィールドの名前(エラー出力用)
  * @param {DataDefinitionView} dataDef データ定義
  */
function addTextField(customFields, customFieldId, customFieldName, dataDef) {
    if (!dataDef.matchDataType('STRING_TEXTFIELD')) {
        throw `Custom Field "${customFieldName}" is Text type. Its value must be set by STRING_TEXTFIELD type data item.`;
    }
    const dataObj = engine.findData(dataDef); // String or null
    const fieldValue = dataObj ?? ''; // String
    Object.assign(customFields, {[customFieldId]: fieldValue});
}

/**
  * 数値型カスタムフィールドの情報を追加する
  * @param {Object} customFields 設定するカスタムフィールド一覧
  * @param {String} customFieldId カスタムフィールドの ID
  * @param {String} customFieldName カスタムフィールドの名前(エラー出力用)
  * @param {DataDefinitionView} dataDef データ定義
  */
function addNumberField(customFields, customFieldId, customFieldName, dataDef) {
    if (!dataDef.matchDataType('DECIMAL')) {
        throw `Custom Field "${customFieldName}" is Number type. Its value must be set by DECIMAL type data item.`;
    }
    const dataObj = engine.findData(dataDef); // BigDecimal or null
    let fieldValue = '';
    if (dataObj !== null) {
        fieldValue = dataObj.doubleValue();
    }
    Object.assign(customFields, {[customFieldId]: fieldValue});
}

/**
  * 日付型カスタムフィールドの情報を追加する
  * @param {Object} customFields 設定するカスタムフィールド一覧
  * @param {String} customFieldId カスタムフィールドの ID
  * @param {String} customFieldName カスタムフィールドの名前(エラー出力用)
  * @param {DataDefinitionView} dataDef データ定義
  */
function addDateField(customFields, customFieldId, customFieldName, dataDef) {
    if (!dataDef.matchDataType('DATE_YMD')) {
        throw `Custom Field "${customFieldName}" is Date type. Its value must be set by DATE_YMD type data item.`;
    }
    const dataObj = engine.findData(dataDef); // AddableDate or null
    let fieldValue = '';
    if (dataObj !== null) {
        fieldValue = dateFormatter.format('MM/dd/yyyy', dataObj);
    }
    Object.assign(customFields, {[customFieldId]: fieldValue});
}

/**
  * 宛先を追加/更新する
  * @param {String} auth 認証設定名
  * @param {Object} contact 宛先情報
  * @return {String} jobId
  */
function upsertContact(auth, contact) {
    const requestBody = {
        "contacts": [contact]
    };
    const response = httpClient.begin()
        .authSetting(auth)
        .body(JSON.stringify(requestBody), 'application/json')
        .put('https://api.sendgrid.com/v3/marketing/contacts');

    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 202) {
        engine.log(responseStr);
        throw `Failed to upsert contact. status: ${status}`;
    }
    return JSON.parse(responseStr).job_id;
}

/**
  * ジョブの処理状況を確認する
  * @param {String} auth 認証設定名
  * @param {String} jobId ジョブ ID
  * @return {boolean} true: 完了, false: 未完了
  */
function checkStatus(auth, jobId) {
    const response = httpClient.begin()
        .authSetting(auth)
        .get(`https://api.sendgrid.com/v3/marketing/contacts/imports/${jobId}`);

    const status = response.getStatusCode();
    const responseStr = response.getResponseAsString();
    if (status !== 200) {
        engine.log(responseStr);
        throw `Failed to get upsert status. status: ${status}`;
    }
    const jobStatus = JSON.parse(responseStr).status;
    switch (jobStatus) {
        case 'pending': // 処理中
            return false;
        case 'completed': // 完了
            return true;
        default: // errored or failed
            engine.log(responseStr);
            throw 'Failed to complete upserting the contact.';
    }
}

    
%d