Skip to main content

Managing Resident Functions

note

Management of custom objects is required for operation of the redaction model from the three-model package.

Depending on your needs and project requirements, you may need to use resident functions for performing some automatic data manipulations or validations in Salesforce.

  1. In the left part of the menu, locate the icon showing available apps and click it.

  2. Start entering InCountry Resident Functions.

    Enter Resident Functions

  3. From the prompted matches, select InCountry Resident Functions.

    Select Resident Functions

On the opened page, you can view what resident function is invoked upon occurrence of a specific trigger.

You can perform the following operations on resident functions:

  • add new resident functions

  • edit the existing resident functions

  • delete the no longer needed resident functions

Prerequisites

All the resident functions are stored on the InCountry platform. You can connect them in the InCountry Data Residency for Salesforce package and further invoke them for specific operations on Salesforce objects.

Before adding a new resident function in Salesforce, please follow these steps:

  1. Create an account on InCountry Portal.

  2. Sign in to InCountry Portal.

  3. Create an environment for Salesforce.

  4. Create a Salesforce service.

  5. Create a Resident Functions service.

  6. Publish a resident function.

note

Use the name of the published resident function script while creating a new InCountry Resident Function in Salesforce.

Adding new resident functions

  1. On the InCountry Resident Functions list, click New.

    Adding new resident functions

  2. In the New Resident Function form, specify the following information:

    1. Serverless Name - enter the name of the resident function that you published on InCountry Portal. Append the '.JS' snippet to the resident function name, for example, resident_function.js.

    2. Object API Name - select the Salesforce object against which a resident function is executed.

    3. Operation Type - select the operation type on the Salesforce object when a resident function is executed.

    4. Additional parameters - specify parameters and values for the resident function as a comma-separated list, for example, RecordTypeId, OwnerId. It’s important that the user has access to these fields and they exist on the record layout.

    5. Active - check the box to enable a resident function. If you clear the box, a resident function will be disabled upon saving.

  3. When complete, click Save.

To save the current resident function and proceed to a new one, click Save & New.

Editing the resident function

  1. On the InCountry Resident Functions list, locate the resident function you want to edit.

  2. Click the down arrow icon.

  3. From the action menu, select Edit.

  4. In the Edit Resident Function form, make the required modifications.

  5. When complete, click Save.

To save the current resident function and proceed to a new one, click Save & New.

Deleting the resident function

  1. On the InCountry Resident Functions list, locate the resident function you want to delete.

  2. Click the down arrow icon.

  3. From the action menu, select Delete.

  4. In the Delete Resident Function form, confirm the removal of the resident function by clicking Delete.

Examples of resident functions

The InCountry Data Residency for Salesforce package supports resident functions that you can use to perform some calculations or validations of regulated data values. In this case, resident functions process regulated data on the InCountry platform in its country of origin, so regulated data does not touch the Salesforce server.

note

After the resident function execution, PII data will be stored according to the policy. This means that the Salesforce database will have hashed values, and the origin values will be stored on the InCountry platform.

Merging multiple values of a record

In this use case, we want to merge multiple values (both regulated and non-regulated values) within the same field and display it in Salesforce. For example, we want to combine values from the First Name, Phone, and Created Date and show them as a single string in the corresponding field. The First Name and Phone field values are stored on the InCountry platform while the Created Date value is stored in the Salesforce database. In the following instructions, we will provide a step-by-step guide on how to implement this scenario.

Instructions

Below you can find the instructions of how to create a formula field through resident functions:

  1. Publish a resident function through a portal or API request by using the following script.

    Script example

    module.exports.handler = async (storage, country, params, modules) => {
    const accountId = params.id;
    const result = {
    data: null,
    error: false,
    errorMsg: null
    };

    let accRecordRaw;
    let accRecordBody;

    try {
    accRecordRaw = await storage.findOne(country, {
    profileKey: accountId
    });
    if (accRecordRaw.record == null) {
    result.error = true;
    result.errorMsg = "Record with Id " + accountId + " was not found";
    } else {
    accRecordBody = JSON.parse(accRecordRaw.record.body);
    accRecordBody.Test_Resident_function__c = params.Name + " - " + params.Phone + " - " + params.CreatedDate;
    }
    } catch (error) {
    result.error = true;
    result.errorMsg = "message: " + error.message + " error-stack: " + error.stack;
    }

    if (result.error == false) {
    try {
    await storage.write(country, {
    key: accountId,
    recordKey: accountId,
    profileKey: accountId,
    body: JSON.stringify(accRecordBody)
    });
    } catch (error) {
    result.error = true;
    result.errorMsg = "message: " + error.message + " error-stack: " + error.stack;
    }

    result.data = JSON.stringify(accRecordBody);
    result.sucess = !result.error;
    }
    return result;
    };
  2. Сreate a new text field within the Account object. For example : Test_Resident_function__c

    1. Test_Resident_function__c = FirstName + ”, ” + Phone + ”, Created at: ” + CreatedDate

      1. FirstName - PII field (String).

      2. Phone - PII field (String).

      3. CreatedDate - non-PII field (DATETIME - this parameter must be localized).

  3. Configure this field like a protected field in the package settings.

  4. Add a new InCountry resident function for the Account object. The resident function name must match the resident function script name. Don't forget to specify additional parameters if needed. (It can be any field within the Account object).

    note

    When adding a new resident function, please append the '.JS' snippet to the resident function name, for example, resident_function.js.

  5. Try to update an account and its protected fields used by the resident function script to ensure that the resident function is executed correctly and the formula field is populated with the correct values.

Validating the mobile phone number against the pre-defined pattern

In this use case, we want to implement the validation of the mobile phone number against the pre-defined pattern when creating a new contact. If this phone number is valid, the Mobile_Valid__c custom field of a checkbox type is marked as true, otherwise it is marked as false. The Phone field is a regulated field and its original value is stored on the InCountry platform.

WHEN a new contact is created, the flow checks the following conditions:

  • Mobile phone is valid according to the template /^+?[1][0-9]{10}$/.

THEN

  • Mobile_Valid__c = true;

Instructions

To validate the mobile phone number against the pre-defined pattern, follow these steps:

  1. Publish a resident function through a portal or API request by using the following script.

    Script example

    module.exports.handler = async function(storage, country, params, modules) {
    const pattern = /^[+](86)?[1][0-9]{10}$/;
    const customerId = params.id;
    const SALESFORCE_WS_ENDPOINT = "https://your_org_domain.my.salesforce.com/services/data/v54.0/sobjects/Contact/" + customerId;

    let customerRecordRaw;
    let customerRecordBody;
    let result = { data: "", error: false, errorMsg: null };

    /* Helper Functions */
    const getCalloutHeaders = () => {
    return { headers: {
    Authorization: "Bearer " + params.token,
    "Content-Type": "application/json",
    }};
    }

    const hanldeError = (error) => {
    result.error = true;
    result.errorMsg = error.message;
    }
    /* Helper Functions */

    try {
    customerRecordRaw = await storage.findOne(country, { profileKey: customerId });
    customerRecordBody = JSON.parse(customerRecordRaw.record.body);
    let validateResult = pattern.test(customerRecordBody.MobilePhone);

    await modules.axios.patch(
    SALESFORCE_WS_ENDPOINT,
    { Mobile_Valid__c: validateResult },
    getCalloutHeaders()
    );
    } catch (error) {
    hanldeError(error);
    }

    result.data = !result.error
    ? "Mobile is valid"
    : result.errorMsg;

    return result;
    };
  2. On the Settings page, add a policy for the Contact Salesforce object.

  3. Configure the Mobile Phone field as a regulated PII field.

  4. Add a new InCountry resident function for the Contact object as shown above. The resident function name must match the resident function script name. Don't forget to specify additional parameters if needed. (It can be any field within the Contact object).

  5. In the New Resident Function form, specify the following information:

    1. In the Serverless Name box, enter flow_demo.js.

    2. In the Object API Name box, select Contact.

    3. In the Operation Type box, select Create.

    4. In the Additional parameters box, enter MobilePhone.

    5. Check the Active box to enable a resident function.

  6. When complete, click Save.

Replacing the formula-type fields with the resident function

note

The formula-field should be configured as read-only on UI, to avoid accidental changes and subsequent issues.

In this use case, we want to replace the formula-type fields with the resident function.

Formula-field example (advanced type):

IF(
AND(
FirstName = "John",
LastName = "Doe",
Title = "CEO",
Website = SUBSTITUTE(Email, LEFT(Email, FIND("@", Email)), "www.")
),
"High",
"Low"
)

WHEN a new lead is created, the formula-field checks the following conditions:

THEN

  • Priority_Text__c = "High";

ELSE

  • Priority_Text__c = "Low";

Instructions

To replace the formula-type fields with the resident function, follow these steps:

  1. Publish a resident function through a portal or API request by using the following script.

    Script example

    module.exports.handler = async (storage, country, params, modules) => {
    const leadId = params.id;
    const result = { data: null, success: false, error: false, errorMsg: null };

    let leadRecordRaw;
    let leadRecordBody;

    const formulaCondition =
    params.FirstName == "John" &&
    params.LastName == "Doe" &&
    params.Title == "CEO" &&
    checkDomain();

    /* Helper Functions */
    const runFormulaLogic = () => {
    leadRecordBody.Priority_Text__c = formulaCondition ?
    "High" :
    "Low";
    }

    const checkDomain = () => {
    // e.g. john.doe@incountry.com -> ['john.doe', 'incountry.com']
    let eDomain = params.Email.split('@');
    // e.g. www.incountry.com -> ['', 'incountry.com'']
    let wDomain = params.Website.split('www.');

    return eDomain[1] == wDomain[1];
    }

    const hanldeError = (error) => {
    result.error = true;
    result.errorMsg = error.message;
    }
    /* Helper Functions */

    try {
    leadRecordRaw = await storage.findOne(country, {
    profileKey: leadId
    });

    if (!leadRecordRaw) {
    throw new Error("Record with Id " + leadId + " was not found");
    }

    leadRecordBody = JSON.parse(leadRecordRaw.record.body);
    runFormulaLogic();

    await storage.write(country, {
    key: leadId,
    recordKey: leadId,
    profileKey: leadId,
    body: JSON.stringify(leadRecordBody)
    });
    } catch (error) {
    hanldeError(error);
    }

    result.data = JSON.stringify(leadRecordBody);
    result.sucess = !result.error;
    return result;
    };
  2. On the Settings page, add a policy for the lead Salesforce object.

  3. Configure the following fields as regulated fields:

    1. FirstName

    2. LastName

    3. Title

    4. Website

    5. Email

    6. Priority_Text__c (Text)

  4. Add a new InCountry resident function for the lead object as shown above. The resident function name must match the resident function script name. Don't forget to specify additional parameters if needed. (It can be any field within the Lead object).

  5. In the New Resident Function form, specify the following information:

    1. In the Serverless Name box, enter formula_field_demo.js

    2. In the Object API Name box, select Lead.

    3. In the Operation Type box, select Create.

    4. In the Additional parameters box, specify the following parameters: FirstName,LastName,Title,Website,Email,Priority_Text__c.

    5. Check the Active box to enable a resident function.

  6. When complete, click Save.

Converting leads with the resident function

In this use case, we want to convert a lead into an account, contact, or opportunity using the resident function.

Instructions

  1. Publish a resident function through a portal or API request by using the following script.

    Script example

    module.exports.handler = async function(storage, country, params, modules) {

    /* Helper Function */
    const hanldeError = (error) => {
    result.error = true;
    result.errorMsg = error.message;
    }

    const result = { data: null, success: false, error: false, errorMsg: null };
    const leadId = params.id;

    const leadConvert = params.leadConvert ? JSON.parse(params.leadConvert) : {};
    const {
    "new": {
    accountId: newAccountId,
    contactId: newContactId,
    opportunityId: newOpportunityId
    } = {},
    "old": {
    accountId: oldAccountId,
    contactId: oldContactId,
    opportunityId: oldOpportunityId
    } = {}
    } = leadConvert || {};

    try {
    let accountRecord = await storage.findOne(country, {
    profileKey: oldAccountId
    });

    if (!accountRecord) {
    throw new Error("Record with Id " + oldAccountId + " was not found");
    }

    await storage.write(country, {
    key: oldAccountId,
    recordKey: oldAccountId,
    profileKey: oldAccountId,
    body: JSON.stringify({
    Description: 'Test Description',
    Name: 'Updated Name'
    })
    });
    } catch (error) {
    hanldeError(error);
    }

    result.data = JSON.stringify({
    leadId,
    newAccountId,
    newContactId,
    newOpportunityId,
    oldAccountId,
    oldContactId,
    oldOpportunityId
    });
    result.sucсess = !result.error;

    return result;
    };

    Representation of the leadConvert parameter

    A resident function is executed once the leadConvert parameter has been passed to it. This parameter includes two objects:

    • new - IDs of created records:

      • accountId - identifier of the Account record

      • contactId - identifier of the Contact record

      • opportunityId - identifier of the Opportunity record

    • old - IDs of existing records selected during the lead conversion:

      • accountId - identifier of the Account record

      • contactId - identifier of the Contact record

      • opportunityId - identifier of the Opportunity record

  2. Add a new resident function for the lead object in Salesforce as shown above. The resident function name must match the resident function script name.

  3. In the New Resident Function form, specify the following information:

    1. In the Serverless Name box, enter lead_convert_demo.js

    2. In the Object API Name box, select Lead.

    3. In the Operation Type box, select Lead Convert.

    4. Check the Active box to enable a resident function.

  4. When complete, click Save.