Issue i face after update to FDK 9 and node 18

Hello,
we have a custom app (serverless) for freshworks CRM which not work on production so i just went to doc and found that we have to update fdk and node version to run app on fresh work we found this on Quick Start

steps that i follow for upgrade:

add <script src="{{{appclient}}}"></script> to iparams.html file for latest version.
switch to 2.3 version form 2.2 .

  1. install fdk 9 via this npm install https://cdn.freshdev.io/fdk/latest.tgz -g
  2. removed whitelisted-domains from manifest.json.
  3. add all api call from client.request to client.request.invokeTemplate for
    iparams.js file.
  4. change server.js request file to $request.invokeTemplate (as stated in doc)
  5. add all client.request and $request.invokeTemplate call to request.json file.

now expected is app should run but i get issue on parsing server.js file is
syntax error: $request is undefined.
here is my manifest.json file for reference.

{
  "platform-version": "2.3",
  "product": {
    "freshworks_crm": {
      "events": {
        "onContactCreate": {
          "handler": "onContactCreateCallback"
        },
        "onContactUpdate": {
          "handler": "onContactUpdateCallback"
        },
        "onDealCreate": {
          "handler": "onDealCreateCallback"
        },
        "onDealUpdate": {
          "handler": "onDealUpdateCallback"
        },
        "onAppointmentCreate": {
          "handler": "onAppointmentCreateCallback"
        },
        "onTaskCreate": {
          "handler": "onTaskCreateCallback"
        },
        "onAppInstall": {
          "handler": "onAppInstallCallback"
        },
        "onAppUninstall": {
          "handler": "onAppUninstallCallback"
        }
      },
      "requests": {
        "apiCallGet": {}, // this I use in iparams.js (working fine)
        "msg91ApiCallGet": {}, // this I use in iparams.js (working fine)
        "msg91ApiCallPost": {}, // this I use in iparams.js (working fine)
        "getNthPageData": {}, // this I use in iparams.js (working fine)
        "getAllTags": {}, // this I use in iparams.js (working fine)
        "getDealsActivity": {}, // this I use in server.js 
        "getSalesActivity": {}, // this I use in server.js 
        "sendCampaign": {} // this I use in server.js 
      }
    }
  },
  "dependencies": {
    "request": "2.88.0",
    "node-fetch": "2.6.1"
  },
  "engines": {
    "node": "18.17.1",
    "fdk": "9.0.5"
  }
}

note : i am also using node fetch in server.js also.

Any help would be apreciable.

Hi @Mohit_sharma,

Do you get this error when running the app or testing any particular functionality after running the app?

hii @Raviraj ,
on time of running the app while server.js is in process of parse, and some time it work but when execution of code reach to $request in server.js file.

@Mohit_sharma Can you do the app installation and iparams submission via end-to-end testing and confirm if the issue repeats?

If it repeats, we might need the files in the server folder to check further. I will create a private direct message thread to seek the app files.

Hi Raviraj
Here is the actual issue I face on local run I get "Error while executing App server script! - $request.invokeTemplate(…).then(…).catch is not a function”

It’s when we test after install app on http://localhost:10001/web/test with event onContactCreate.
And
On end to end encryption testing I am getting this error here is video of it.

The views and opinions included in this email belong to their author and do not necessarily mirror the views and opinions of the company. Our employees are obliged not to make any defamatory clauses, infringe, or authorize infringement of any legal right. Therefore, the company will not take any liability for such statements included in emails. In case of any damages or other liabilities arising, employees are fully responsible for the content of their emails.

(Attachment Screen Recording 2023-11-29 at 7.33.13 PM.mov is missing)

@Mohit_sharma The same scenario works for me on FDK 9.0.6.

The video is not attached to the post since you have responded via email.

Anyway, I will have to take a look at your server.js code to see what could be different and to replicate the same. Could you please share the server.js code without any sensitive information and any unnecessary code that can be run?

Hi Raviraj ,
I am getting this error while testing app, and the video formate is not supported by your fresworks domain I got mail that uploaded file is not same as allowed file formate.
Here is code of server.js you can check it, my mail server not allow file to be share so I am sending full code here

const request = require(“request”)
const fetch = require(“node-fetch”);
var EMAIL_REGEX_PATTERN =
/^(([^<>()\.,;:\s@"]+(.[^<>()\.,;:\s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$/;
const socketURL = “apiary”;
exports = {
onContactCreateCallback: function (payload) {
createContactEvent(payload);
},
onContactUpdateCallback: function (payload) {
let changes = payload.data.changes.model_changes;
if (payload.data.contact.is_deleted) {
updateContactEvents(payload, “delete_contact”);
} else if (changes.contact_status_id != undefined) {
updateContactEvents(payload, “change_stage”);
} else if (changes.tags !== undefined) {
updateContactEvents(payload, “change_tag”);
} else {
updateContactEvents(payload, “change_lead”);
}
},
onDealCreateCallback: function (payload) {
dealEvent(payload, “new_deal”);
},
onDealUpdateCallback: function (payload) {
let changes = payload.data.changes.model_changes;
if (payload.data.deal.is_deleted) {
dealEvent(payload, “delete_deal”);
} else if (changes.deal_stage_id !== undefined) {
dealEvent(payload, “deal_stage”);
} else if (changes.tags !== undefined) {
dealEvent(payload, “change_deal_tag”);
} else {
dealEvent(payload, “update_deal”);
}
},
onAppointmentCreateCallback: function (payload) {
taskAndAppointementEvent(payload, “appointment”);
},
onTaskCreateCallback: function (payload) {
taskAndAppointementEvent(payload, “task”);
},
onAppInstallCallback: function (payload) {
const companyInfo = {
account_info: payload?.iparams?.account_info,
event: payload?.event,
account_id: payload?.account_id,
domain: payload?.domain,
fs_api_key: payload?.iparams?.fs_api_key,
msg_auth: payload?.iparams?.msg_auth,
region: payload?.region,
};
appInstalled(companyInfo);
},
onAppUninstallCallback: function (payload) {
const companyInfo = {
account_info: payload?.iparams?.account_info,
event: payload?.event,
account_id: payload?.account_id,
domain: payload?.domain,
fs_api_key: payload?.iparams?.fs_api_key,
msg_auth: payload?.iparams?.msg_auth,
region: payload?.region,
};
appUninstalled(companyInfo);
},
};

async function postApiData(url, data) {
try {
const response = await fetch(url, {
method: “POST”,
headers: {
“Content-Type”: “application/json”,
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(HTTP error! status: ${response.status});
}
const json = await response.json();
return json;
} catch (error) {
console.error(“Error:”, error);
}
}

function appInstalled(payload) {
const api = postApiData(socketURL, payload);
api.then(
(res) => {
if (res.success) {
renderData();
}
},
(err) => {
renderData(err);
}
);
}

function appUninstalled(payload) {
const api = postApiData(socketURL, payload);
api.then(
(res) => {
if (res.success) {
renderData();
}
},
(err) => {
renderData(err);
}
);
}

function taskAndAppointementEvent(payload, eventType) {
// dumpPayload(payload);
if (eventType === “task”) {
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key === “sales”) {
handleSalesActivities(payload, value, eventType);
}
}
});
} else if (eventType === “appointment”) {
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key === “sales”) {
handleSalesActivities(payload, value, eventType);
}
}
});
}
}

function dealEvent(payload, eventType) {
if (eventType === “new_deal”) {
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key !== eventType) return;
handleDealEvent(payload, value, “new_deal”, “deal”);
}
});
} else if (eventType === “delete_deal”) {
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key !== eventType) return;
handleDealEvent(payload, value, eventType, “deal”);
}
});
} else if (eventType === “update_deal”) {
const changesKey = Object.keys(payload.data.changes.model_changes);
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key !== eventType) return;
let triggerUpdateForFields = value.selected_fields;
for (let i = 0; i <= triggerUpdateForFields.length; i++) {
if (changesKey.includes(triggerUpdateForFields[i])) {
handleDealEvent(payload, value, eventType, “deal”);
break;
}
}
}
});
} else if (eventType === “deal_stage”) {
const changesObject = payload.data.changes.model_changes;
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key !== eventType) return;
let triggerUpdateForFields = value.selected_ids;
for (let i = 0; i <= triggerUpdateForFields.length; i++) {
if (
changesObject?.[“deal_stage_id”]?.includes(
+triggerUpdateForFields[i]
)
) {
handleDealEvent(payload, value, eventType, “deal”);
break;
}
}
}
});
} else if (eventType === “change_deal_tag”) {
const changesObject = payload.data.changes.model_changes;
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key !== eventType) return;
// const addedTags = changesObject?.tags?.added
// ? changesObject?.tags?.[“added”]?.map((tag) => tag.name.toLowerCase())
// : ;
const addedTags = changesObject?.tags?.[“added”]?.map((tag) =>
tag.name.toLowerCase()
);
let allTags = […addedTags];
let triggerUpdateForFields = value.selected_fields.map((field) =>
field.toLowerCase()
);
for (let i = 0; i <= triggerUpdateForFields.length; i++) {
if (allTags.includes(triggerUpdateForFields[i])) {
handleDealEvent(payload, value, eventType, “deal”);
break;
}
}
}
});
}
}

function createContactEvent(payload) {
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key === “signup”)
handleContact(payload, value, “signup”, “contact”, null);
}
});
}

function updateContactEvents(payload, updateType) {
console.log(‘payload’, updateType)
const changeModel = Object.keys(payload.data.changes.model_changes);
const changesObject = payload.data.changes.model_changes;
if (updateType === “change_lead”) {
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key !== “change_lead”) return;
let triggerUpdateForFields = value.selected_fields;
for (let i = 0; i <= triggerUpdateForFields.length; i++) {
if (changeModel.includes(triggerUpdateForFields[i])) {
handleContact(payload, value, updateType, “contact”, “update”);
break;
}
}
}
});
} else if (updateType === “change_tag”) {
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key !== updateType) return;
// const addedTags = changesObject?.tags?.added
// ? changesObject?.tags?.[“added”]?.map((tag) => tag.name.toLowerCase())
// : ;
const addedTags = changesObject?.tags?.[“added”]?.map((tag) =>
tag.name.toLowerCase()
);
let allTags = […addedTags];
let triggerUpdateForFields = value.selected_fields.map((field) =>
field.toLowerCase()
);
for (let i = 0; i <= triggerUpdateForFields.length; i++) {
if (allTags.includes(triggerUpdateForFields[i])) {
handleContact(payload, value, updateType, “contact”, “update”);
break;
}
}
}
});
} else if (updateType === “change_stage”) {
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key !== updateType) return;
let triggerUpdateForFields = value.selected_ids;
for (let i = 0; i <= triggerUpdateForFields.length; i++) {
if (
changesObject?.[“contact_status_id”]?.includes(
+triggerUpdateForFields[i]
)
) {
handleContact(payload, value, updateType, “contact”, “update”);
break;
}
}
}
});
} else if (updateType === “delete_contact”) {
payload.iparams.allEvents.forEach((event) => {
for (const [key, value] of Object.entries(event)) {
if (key !== updateType) return;
handleContact(payload, value, updateType, “contact”, “delete”);
}
});
}
}

/**

  • Function to handle create/update contact

@Mohit_sharma The attached code is partial. Can you please trim the code to only the necessary parts relevant to the error and share it?

Hi raviraj,

Every where i use $request.invokeTemplate in file will cause error Error while executing App server script! - $request.invokeTemplate(…).then(…).catch is not a function

Which belong to events that are in export at top of file.

const request = require("request")
const fetch = require("node-fetch");
var EMAIL_REGEX_PATTERN =
  /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const socketURL = "https://ebl-api-h7duexlbuq-el.a.run.app/func/gzLWpRYwbW5l";
exports = {
  onContactCreateCallback: function (payload) {
    createContactEvent(payload);
  },
  onContactUpdateCallback: function (payload) {
    let changes = payload.data.changes.model_changes;
    if (payload.data.contact.is_deleted) {
      updateContactEvents(payload, "delete_contact");
    } else if (changes.contact_status_id != undefined) {
      updateContactEvents(payload, "change_stage");
    } else if (changes.tags !== undefined) {
      updateContactEvents(payload, "change_tag");
    } else {
      updateContactEvents(payload, "change_lead");
    }
  },
  onDealCreateCallback: function (payload) {
    dealEvent(payload, "new_deal");
  },
  onDealUpdateCallback: function (payload) {
    let changes = payload.data.changes.model_changes;
    if (payload.data.deal.is_deleted) {
      dealEvent(payload, "delete_deal");
    } else if (changes.deal_stage_id !== undefined) {
      dealEvent(payload, "deal_stage");
    } else if (changes.tags !== undefined) {
      dealEvent(payload, "change_deal_tag");
    } else {
      dealEvent(payload, "update_deal");
    }
  },
  onAppointmentCreateCallback: function (payload) {
    taskAndAppointementEvent(payload, "appointment");
  },
  onTaskCreateCallback: function (payload) {
    taskAndAppointementEvent(payload, "task");
  },
  onAppInstallCallback: function (payload) {
    const companyInfo = {
      account_info: payload?.iparams?.account_info,
      event: payload?.event,
      account_id: payload?.account_id,
      domain: payload?.domain,
      fs_api_key: payload?.iparams?.fs_api_key,
      msg_auth: payload?.iparams?.msg_auth,
      region: payload?.region,
    };
    appInstalled(companyInfo);
  },
  onAppUninstallCallback: function (payload) {
    const companyInfo = {
      account_info: payload?.iparams?.account_info,
      event: payload?.event,
      account_id: payload?.account_id,
      domain: payload?.domain,
      fs_api_key: payload?.iparams?.fs_api_key,
      msg_auth: payload?.iparams?.msg_auth,
      region: payload?.region,
    };
    appUninstalled(companyInfo);
  },
};

async function postApiData(url, data) {
  try {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    });
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const json = await response.json();
    return json;
  } catch (error) {
    console.error("Error:", error);
  }
}

function appInstalled(payload) {
  const api = postApiData(socketURL, payload);
  api.then(
    (res) => {
      if (res.success) {
        renderData();
      }
    },
    (err) => {
      renderData(err);
    }
  );
}

function appUninstalled(payload) {
  const api = postApiData(socketURL, payload);
  api.then(
    (res) => {
      if (res.success) {
        renderData();
      }
    },
    (err) => {
      renderData(err);
    }
  );
}

function taskAndAppointementEvent(payload, eventType) {
  // dumpPayload(payload);
  if (eventType === "task") {
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key === "sales") {
          handleSalesActivities(payload, value, eventType);
        }
      }
    });
  } else if (eventType === "appointment") {
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key === "sales") {
          handleSalesActivities(payload, value, eventType);
        }
      }
    });
  }
}

function dealEvent(payload, eventType) {
  if (eventType === "new_deal") {
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key !== eventType) return;
        handleDealEvent(payload, value, "new_deal", "deal");
      }
    });
  } else if (eventType === "delete_deal") {
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key !== eventType) return;
        handleDealEvent(payload, value, eventType, "deal");
      }
    });
  } else if (eventType === "update_deal") {
    const changesKey = Object.keys(payload.data.changes.model_changes);
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key !== eventType) return;
        let triggerUpdateForFields = value.selected_fields;
        for (let i = 0; i <= triggerUpdateForFields.length; i++) {
          if (changesKey.includes(triggerUpdateForFields[i])) {
            handleDealEvent(payload, value, eventType, "deal");
            break;
          }
        }
      }
    });
  } else if (eventType === "deal_stage") {
    const changesObject = payload.data.changes.model_changes;
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key !== eventType) return;
        let triggerUpdateForFields = value.selected_ids;
        for (let i = 0; i <= triggerUpdateForFields.length; i++) {
          if (
            changesObject?.["deal_stage_id"]?.includes(
              +triggerUpdateForFields[i]
            )
          ) {
            handleDealEvent(payload, value, eventType, "deal");
            break;
          }
        }
      }
    });
  } else if (eventType === "change_deal_tag") {
    const changesObject = payload.data.changes.model_changes;
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key !== eventType) return;
        // const addedTags = changesObject?.tags?.added
        //   ? changesObject?.tags?.["added"]?.map((tag) => tag.name.toLowerCase())
        //   : [];
        const addedTags = changesObject?.tags?.["added"]?.map((tag) =>
          tag.name.toLowerCase()
        );
        let allTags = [...addedTags];
        let triggerUpdateForFields = value.selected_fields.map((field) =>
          field.toLowerCase()
        );
        for (let i = 0; i <= triggerUpdateForFields.length; i++) {
          if (allTags.includes(triggerUpdateForFields[i])) {
            handleDealEvent(payload, value, eventType, "deal");
            break;
          }
        }
      }
    });
  }
}

function createContactEvent(payload) {
  payload.iparams.allEvents.forEach((event) => {
    for (const [key, value] of Object.entries(event)) {
      if (key === "signup")
        handleContact(payload, value, "signup", "contact", null);
    }
  });
}

function updateContactEvents(payload, updateType) {
  console.log('payload', updateType)
  const changeModel = Object.keys(payload.data.changes.model_changes);
  const changesObject = payload.data.changes.model_changes;
  if (updateType === "change_lead") {
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key !== "change_lead") return;
        let triggerUpdateForFields = value.selected_fields;
        for (let i = 0; i <= triggerUpdateForFields.length; i++) {
          if (changeModel.includes(triggerUpdateForFields[i])) {
            handleContact(payload, value, updateType, "contact", "update");
            break;
          }
        }
      }
    });
  } else if (updateType === "change_tag") {
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key !== updateType) return;
        // const addedTags = changesObject?.tags?.added
        //   ? changesObject?.tags?.["added"]?.map((tag) => tag.name.toLowerCase())
        //   : [];
        const addedTags = changesObject?.tags?.["added"]?.map((tag) =>
          tag.name.toLowerCase()
        );
        let allTags = [...addedTags];
        let triggerUpdateForFields = value.selected_fields.map((field) =>
          field.toLowerCase()
        );
        for (let i = 0; i <= triggerUpdateForFields.length; i++) {
          if (allTags.includes(triggerUpdateForFields[i])) {
            handleContact(payload, value, updateType, "contact", "update");
            break;
          }
        }
      }
    });
  } else if (updateType === "change_stage") {
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key !== updateType) return;
        let triggerUpdateForFields = value.selected_ids;
        for (let i = 0; i <= triggerUpdateForFields.length; i++) {
          if (
            changesObject?.["contact_status_id"]?.includes(
              +triggerUpdateForFields[i]
            )
          ) {
            handleContact(payload, value, updateType, "contact", "update");
            break;
          }
        }
      }
    });
  } else if (updateType === "delete_contact") {
    payload.iparams.allEvents.forEach((event) => {
      for (const [key, value] of Object.entries(event)) {
        if (key !== updateType) return;
        handleContact(payload, value, updateType, "contact", "delete");
      }
    });
  }
}

/**
 * Function to handle create/update contact
 *
 * @param {*} payload
 * @param {*} triggeredEvents // change_lead
 * @param {*} platform // contact
 * @param {*} type // delete | update
 */
function handleContact(payload, eventPayload, triggeredEvent, platform, type) {
  let event = eventPayload.enabler;
  if (event && eventPayload["variables"]) {
    let filteredData = [];
    if (type === "change_lead" || type === "delete_contact") {
      filteredData = eventPayload["variables"].filter(
        (data) => data.type == type
      );
    } else {
      filteredData = eventPayload["variables"];
    }

    let msg91AuthKey = payload.iparams.msg_auth;
    let campaign = eventPayload.campaign;
    let sendTo = eventPayload.sendToFields;
    let variables = [];

    if (filteredData && filteredData?.length > 0) {
      filteredData.map((data) => {
        let fwField = data?.fw_field;
        let fieldValue = "";
        if (fwField && fwField.slice(0, 3) == "cf_") {
          let matched = payload.data[platform].custom_fields.filter(
            (data) => data.name == fwField
          );
          fieldValue = matched.length > 0 ? matched[0].value : "";
        } else if (payload.data[platform] && payload.data[platform][fwField]) {
          if (data.field_type === "dropdown") {
            let value = payload.data[platform][fwField]?.value;
            let choice = data?.choices?.find((choice) => choice.id == value);
            fieldValue = choice?.value;
          } else if (data.field_type === "multi_select_dropdown") {
            const values = payload.data[platform][fwField].value.map(
              (val) => val.value
            );
            fieldValue = values.join(",");
          } else if (
            fwField === "emails" &&
            data.field_type === "group_field"
          ) {
            let values;

            if (payload.data[platform][fwField].value) {
              values = payload.data[platform][fwField].value.filter(
                (val) => val.is_primary === true
              );
            } else {
              values = payload.data[platform][fwField].filter(
                (val) => val.is_primary === true
              );
            }
            fieldValue = values?.[0]?.email;
          } else {
            fieldValue = payload.data[platform][fwField]?.value;
          }
        }
        variables[data?.campaign_field] = getFieldValue(fieldValue);
      });
    }

    if (
      payload.data[platform] &&
      Object.keys(payload.data[platform]).length > 0
    ) {
      Object.keys(payload.data[platform]).forEach((key) => {
        if (isEmailMobileField(key)) {
          sendTo = getSendToData(sendTo, key, payload.data[platform][key]);
        }
      });

      if (
        payload.data[platform].custom_fields &&
        Object.keys(payload.data[platform].custom_fields).length > 0
      ) {
        Object.keys(payload.data[platform].custom_fields).forEach((key) => {
          if (
            isEmailMobileField(payload.data[platform].custom_fields[key].name)
          ) {
            sendTo = getSendToData(
              sendTo,
              payload.data[platform].custom_fields[key].name,
              payload.data[platform].custom_fields[key]
            );
          }
        });
      }
    }
    sendCampaign(msg91AuthKey, campaign, sendTo, variables);
  } else {
    // event is inactive/not available
  }
}

function getFieldValue(fieldValue) {
  // let fieldValues = [];
  // if (fieldValue && typeof fieldValue === "object" && fieldValue.length > 0) {
  //   fieldValue.forEach((field) => {
  //     fieldValues.push(field.value);
  //   });
  //   return fieldValues.join(", ");
  // } else {
  //   return fieldValue;
  // }

  return fieldValue;
}

function getSendToData(sendTo, fwField, fieldValue) {
  if (!fieldValue) return;
  let sendToData = [];
  if (sendTo && sendTo.to) {
    let loop = 0;
    sendTo.to.forEach((to) => {
      if (to == fwField) {
        // if (
        //   fieldValue.value &&
        //   typeof fieldValue.value === "object" &&
        //   !Array.isArray(fieldValue.value) &&
        //   fieldValue.value.length > 0
        // ) {
        //   fieldValue.value.forEach((field) => {
        //     sendToData[loop] = field.value;
        //     loop++;
        //   });
        // }
        if (Array.isArray(fieldValue)) {
          fieldValue.forEach((field) => {
            sendToData[loop] = `mapped_${field?.value}`;
            loop++;
          });
        } else if (
          fieldValue.value &&
          Array.isArray(fieldValue.value) &&
          fieldValue.value.length > 0
        ) {
          fieldValue.value.forEach((field) => {
            sendToData[loop] = `mapped_${field?.email || field?.value}`;
            loop++;
          });
        }

        /////////  else if (typeof fieldValue === "string" && !isValidEmail(fieldValue))
        else if (typeof fieldValue === "string") {
          sendToData[loop] = `mapped_${fieldValue}`;
          loop++;
        } else {
          sendToData[loop] = `mapped_${fieldValue?.value}`;
          loop++;
        }
      } else {
        sendToData[loop] = to;
        loop++;
      }
    });
  }
  sendTo.to = sendToData;
  return sendTo;
}

function isEmailMobileField(fieldName) {
  if (fieldName.indexOf("email") > -1 || fieldName.indexOf("mobile") > -1) {
    return true;
  } else {
    return false;
  }
}

/**
 * Checks if email is valid
 *
 * @param {*} email
 * @returns
 */
function isValidEmail(email) {
  if (EMAIL_REGEX_PATTERN.test(email)) {
    return true;
  } else {
    return false;
  }
}

function isNumeric(str) {
  if (typeof str != "string") return false;
  return !isNaN(str) && !isNaN(parseFloat(str));
}

/**
 * Handles sales activities
 * @param {*} payload
 * @param {*} subEvent // appointment | task
 */
function handleSalesActivities(payload, eventPayload, subEvent) {
  let event = eventPayload.enabler;
  if (
    event &&
    payload.data[subEvent].targetable_id !== null &&
    payload.data[subEvent].targetable_type !== null
  ) {
    let type = payload.data[subEvent].targetable_type.toLowerCase();
    let filteredData = [];
    filteredData = eventPayload.variables.filter(
      (data) => data.type == subEvent && data.fw_field_group == type
    );
    getSalesActivity(payload, subEvent)
      .then((salesData) => {
        salesEventTriggered(
          payload,
          eventPayload,
          salesData[type],
          filteredData
        );
      })
      .catch((err) => {
        renderData(err);
      });
  } else {
    // event is inactive/not available
  }
}

function handleDealEvent(payload, eventPayload, triggeredEvent, platform) {
  let event = eventPayload.enabler;
  if (event && eventPayload.enabler) {
    let filteredData = eventPayload.variables;
    let targetableId = payload?.data?.[platform]?.contact_ids[0];

    getDealsActivity(payload, targetableId)
      .then((dealsData) => {
        dealsEventTriggered(
          payload,
          eventPayload,
          dealsData["contact"],
          filteredData,
          triggeredEvent,
          platform
        );
      })
      .catch((err) => {
        renderData(err);
      });
  } else {
    // event is inactive/not available
  }
}

// Get deals
function getDealsActivity(payload, targetable_id) {
  return new Promise(function (resolve, reject) {
    let id = String(targetable_id);
    let url = `${payload.iparams.fs_domain}/api/contacts/${id}`;
    if (url) {
      $request.invokeTemplate("getDealsActivity" , {
        context: {
          fs_domain: payload.iparams.fs_domain,
          fs_api_key: payload.iparams.fs_api_key,
          id: String(targetable_id)
        }
      }).then((value) => {
          // if (response?.statusCode === 200) {
          //   resolve(JSON.parse(body));
          // } else {
          //   reject(err, "Error on fetching Contact");
          // }
          console.log('getDealsActivity', value)
      }).catch((error) => {
        renderData(error);
      })
    } else {
      reject("Selected record not selected from contacts");
    }
  });
}

function dealsEventTriggered(
  payload,
  eventPayload,
  dealsData,
  filteredData,
  triggeredEvent,
  platform
) {
  let msg91AuthKey = payload.iparams.msg_auth;
  let campaign = eventPayload.campaign;
  let sendTo = eventPayload.sendToFields;

  let variables = [];

  if (filteredData && filteredData.length > 0) {
    filteredData.map(async (data) => {
      let fwField = data.fw_field;
      let fieldValue = "";

      if (fwField && fwField.slice(0, 3) == "cf_") {
        let matched = payload.data[platform].custom_fields.filter(
          (data) => data.name == fwField
        );
        fieldValue = matched.length > 0 ? matched[0].value : "";
      } else if (
        payload.data &&
        payload.data[platform] &&
        payload.data[platform][fwField]
      ) {
        if (data.field_type === "dropdown") {
          let value = payload.data[platform][fwField]?.value;
          let choice = data.choices.find((choice) => choice.id == value);
          fieldValue = choice?.value;
        } else if (data.field_type === "multi_select_dropdown") {
          const values = payload.data[platform][fwField].value.map(
            (val) => val?.value
          );
          fieldValue = values.join(",");
        } else {
          fieldValue = payload.data[platform][fwField].value;
        }
      }

      variables[data.campaign_field] = getFieldValue(fieldValue);
    });
  }

  if (dealsData && Object.keys(dealsData).length > 0) {
    Object.keys(dealsData).forEach((key) => {
      if (isEmailMobileField(key)) {
        if (!dealsData[key]) return;
        sendTo = getSendToData(sendTo, key, dealsData[key]);
      }
    });
  }
  sendCampaign(msg91AuthKey, campaign, sendTo, variables);
}

function getSalesActivity(payload, activity) {
  return new Promise(function (resolve, reject) {
    let subEvent = payload.data[activity].targetable_type.toLowerCase();
    let id = payload.data[activity].targetable_id;
    if (subEvent != null && id != null) {
      let url = "";
      if (subEvent == "contact") {
        url = `${payload.iparams.fs_domain}/api/contacts/${id}`;
      }
      if (url) {
        $request.invokeTemplate("getSalesActivity" , {
          context: {
            fs_domain: payload.iparams.fs_domain,
            fs_api_key: payload.iparams.fs_api_key,
            id: String(targetable_id)
          }
        }).then((value) => {
            // if (response?.statusCode === 200) {
            //   resolve(JSON.parse(body));
            // } else {
            //   reject(err, "Error on fetching Contact");
            // }
            console.log('getSalesActivity', value)
        }).catch((error) => {
          reject("Error on fetch", error)
          // reject("Error on fetch " + subEvent);
        })
      } else {
        reject("Selected record not selected from " + activity);
      }
    } else {
      reject("Record not selected from " + activity);
    }
  });
}

/**
 * Triggers sales event
 * @param {*} payload
 * @param {*} salesData
 * @param {*} filteredData
 */
function salesEventTriggered(payload, eventpayload, salesData, filteredData) {
  let msg91AuthKey = payload.iparams.msg_auth;
  let campaign = eventpayload.campaign;
  let sendTo = eventpayload.sendToFields;

  let variables = [];

  if (filteredData && filteredData.length > 0) {
    filteredData.map((data) => {
      let fwField = data.fw_field;
      let fieldValue = "";
      if (fwField.slice(0, 3) == "cf_") {
        fieldValue = salesData.custom_field[fwField];
      } else {
        if (data.field_type === "dropdown") {
          let value = salesData?.[fwField];
          let choice = data.choices.find((choice) => choice.id == value);
          fieldValue = choice?.value || null;
        } else {
          fieldValue = salesData[fwField];
        }
      }
      variables[data.campaign_field] = getFieldValue(fieldValue);
    });
  }

  if (salesData && Object.keys(salesData).length > 0) {
    Object.keys(salesData).forEach((key) => {
      if (isEmailMobileField(key)) {
        if (!salesData[key]) return;
        sendTo = getSendToData(sendTo, key, salesData[key]);
      }
    });

    // if (
    //   salesData.custom_fields &&
    //   Object.keys(salesData.custom_fields).length > 0
    // ) {
    //   Object.keys(salesData.custom_fields).forEach((key) => {
    //     if (isEmailMobileField(salesData.custom_fields[key])) {
    //       sendTo = getSendToData(sendTo, key, salesData.custom_fields[key]);
    //     }
    //   });
    // }
  }
  sendCampaign(msg91AuthKey, campaign, sendTo, variables);
}

/**
 * Returns list of email/mobiles
 *
 * @param {*} data
 * @returns
 */
function getEmailMobiles(data, isToField = false) {
  let emailMobiles = [];
  if (isToField) {
    let toObject = {};
    if (data && data.length > 0 && Array.isArray(data)) {
      data.forEach((item) => {
        if (item.split("_")?.length === 2) {
          if (isValidEmail(item.split("_")[1])) {
            toObject["email"] = item?.split("_")[1];
          } else if (
            !isValidEmail(item.split("_")[1]) &&
            typeof item !== "object" &&
            isNumeric(item.split("_")[1])
          ) {
            toObject["mobiles"] = item?.split("_")[1];
          }
        } else {
          if (isValidEmail(item)) {
            emailMobiles.push({ email: item });
          } else if (typeof item === "object" && isValidEmail(item?.email)) {
            emailMobiles.push({ email: item?.email });
          } else if (
            !isValidEmail(item) &&
            typeof item !== "object" &&
            isNumeric(item)
          ) {
            emailMobiles.push({ mobiles: item });
          }
        }
      });
    }
    emailMobiles.push(toObject);
  } else {
    if (data && data.length > 0 && Array.isArray(data)) {
      data.forEach((item) => {
        if (isValidEmail(item)) {
          emailMobiles.push({ email: item });
        } else if (typeof item === "object" && isValidEmail(item?.email)) {
          emailMobiles.push({ email: item?.email });
        } else if (
          !isValidEmail(item) &&
          typeof item !== "object" &&
          isNumeric(item)
        ) {
          emailMobiles.push({ mobiles: item });
        }
      });
    }
  }

  emailMobiles = emailMobiles.filter((item) => item.email || item.mobiles);
  return emailMobiles;
}

/**
 * Sends campaign
 *
 * @param {*} auth
 * @param {*} campaign
 * @param {*} sendTo
 * @param {*} variables
 */
function sendCampaign(auth, campaign, sendTo, variables) {
  if (!sendTo.to || !sendTo.to.length || !sendTo.to.flat().length) return;
  let to = [];
  let cc = [];
  let bcc = [];
  let fieldVariables = {};

  Object.keys(variables).forEach((key) => {
    if (typeof variables[key] === "object") {
      // if (variables[key]?.length > 0) {
      //   variables[key] = variables[key][0].value;
      // } else {
      variables[key] = variables[key]?.value;
      // }
    }

    fieldVariables[key] = variables[key];
  });
  if (sendTo) {
    to = getEmailMobiles(sendTo.to, true);
    cc = getEmailMobiles(sendTo.cc);
    bcc = getEmailMobiles(sendTo.bcc);
    $request.invokeTemplate("sendCampaign" , {
      context: {
        campaign: campaign,
        auth: auth
      },
      body: JSON.stringify(
        {
          data: {
            sendTo: [
              {
                to: to,
                cc: cc,
                bcc: bcc,
                variables: fieldVariables,
              },
            ],
          },
        }
      )
    }).then((value) => {
      console.log('sendCampaign', value)
    }).catch((error) => {
      console.log('error 899', error)
    })
  }
}

in this code on every where i use $request.invokeTemplate i got error in command line also and on event api call in network tab.
let me know any changes from my side @Raviraj .

@Raviraj i am eagerly waiting for your response, it is production changes so please assist me for early fixing.

@Raviraj
now issue is solved by my self

what cause issue is i have to wrap $request.invokeTemplate into a try and catch block.

but while installing through end-to-end testing i am getting error, please help me to resolve this.

here is video please check it and suggest us changes

Hey @Mohit_sharma,

Please use $request.invokeTemplate with async-await and also have try-catch blocks when making the request method invocation so that any error can be handled by catch and we can identify the exact issue.

@zach_jones_noel i already mention in my above reply that issue was already solved by using try-catch.
please look at this
Issue i face after update to FDK 9 and node 18 - #12 by Mohit_sharma
comment and video of issue (check console in video), i am facing issue while installing the app in dev mode.

@Mohit_sharma Since this issue is not relevant to the topic originally created, could you please create a new topic for a separate context?

The new issue could be because the renderData() callback was not called properly sooner. We will dig into it in a new topic. Please create one.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.