Expand my Community achievements bar.

SOLVED

Error in Typology rules when set on Recipient

Avatar

Level 3

Hi

We have created Typology rules on Recipient, to check the working hours and based on the Time Zone, send email only during work hrs (9:00 to 17:00).

When I do send a proof, I get the below error.

1826253_pastedImage_2.png

1 Accepted Solution

Avatar

Correct answer by
Level 10

Hi,

Before I start, I'll admit that I've never used a Control typology rule in production before, but here is what I think is happening:

My understanding is that the script in the Control typology rule is run for each delivery it applies to. It is expected that the script will return true or false for each of those deliveries. This means that the Control rule is used to check the validity of the delivery, not details relating to the recipient. See the example here: Control rules.

I believe your script does not actually return true or false because the return statement is in a for block. This means that if there are no items to iterate over, your script will never return anything. This might be the case because I agree with marcoc31069762that you should not be iterating over resultSet but resultSet.recipient. Check out this documentation with examples: SOAP methods in JavaScript​. You can see that if you use operation="select" in your query your result set will contain a list of results stored in a sub-property.

Even if your for block does iterate more than 0 times, it will return true or false for the first recipient in your resultSet (not the recipient of the actual delivery in question) and then exit.

This would explain why your script works in a JS activity (it is valid Javascript), but does not work in a Control rule (since the rule expects true/false). Furthermore, there is a serious performance issue with this script because if I understood correctly, the query would be executed for each delivery. If you have 100k deliveries for example, I think this script would grind your instance to a halt.

Again, that is just my understanding, perhaps someone from staff can confirm or deny this theory

However, here are some general suggestions for your script:

  1. As a best-practice, you should use var to define your variables, otherwise Javascript will create and store them in the global scope. In your browser this is the window object. I am not expert enough to tell you what the global scope is in Campaign, but if you assign values without var, you risk overwriting existing important global properties.
  2. You can optimise this script by assigning local_hh without creating two more Date objects I made the change below.
  3. You can simply return the value of local_hh >= work_start && local_hh <= work_end without doing an if/else, I also made this change below.
  4. By convention, people typically use camelCasel to name variables and functions in Javascript. In your script you use a mix of camelCase, lowercase and snake_case. It makes your script a bit hard to read. I would suggest changing your variable names to camelCase.

var tempQuery = xtk.queryDef.create( < queryDef schema = "nms:recipient"

    operation = "select" > < select > < node expr = "@id" / > < node expr = "@timezone" / > < /select> <where> <condition expr={"@timezone is not null"}/ > < /where></queryDef > );

var resultSet = tempQuery.ExecuteQuery();

for each(var row in resultSet)

{

    var recipientId = row.@id;

    var recipientTz = row.@timezone;

    var today = new Date();

    var localoffset = -(today.getTimezoneOffset() / 60);

    if (recipientTz == "EST") {

        tzdestoffset = -4;

       

    } else if (recipientTz == "CST") {

        tzdestoffset = -5;

       

    } else if (recipientTz == "MST") {

        tzdestoffset = -6;

       

    } else if (recipientTz == "PST") {

        tzdestoffset = -7;

       

    } else {

        logInfo('TimeZone is missing');

    }

   

    var estoffset = tzdestoffset - localoffset;

    var local_hh = today.getHours() + estoffset;

    var local_dttm = new Date(new Date().getTime() + estoffset * 3600 * 1000);

    var local_hh = local_dttm.getHours();

    return local_hh >= work_start && local_hh <= work_end;

    if (local_hh >= work_start && local_hh <= work_end)

    {

        return true;

    } else

    {

        return false;

    }

}

View solution in original post

7 Replies

Avatar

Community Advisor

Hi,

Please share the details of typology rule you have created .

Thanks,

Kapil

Avatar

Level 3

Here is teh typology rules script

var tempQuery = xtk.queryDef.create(<queryDef schema="nms:recipient" operation="select"> <select> <node expr="@id"/> <node expr="@timezone"/> </select> <where> <condition expr={"@timezone is not null"}/> </where></queryDef>);

var resultSet = tempQuery.ExecuteQuery();

for each (var row in resultSet)

{

var recipientId = row.@id;

var recipientTz = row.@timezone;

today = new Date(); 

localoffset = -(today.getTimezoneOffset()/60);

      if (recipientTz=="EST")

      {

            tzdestoffset = -4;

      }

      else if (recipientTz=="CST")

      {

            tzdestoffset = -5;

      }

      else if (recipientTz=="MST")

      {

            tzdestoffset = -6;

      }

      else if (recipientTz=="PST")

      {

            tzdestoffset = -7;

      }

      else

      {

             logInfo('TimeZone is missing');

      }

      estoffset = tzdestoffset-localoffset;

      local_dttm = new Date( new Date().getTime() + estoffset * 3600 * 1000);

      local_hh = local_dttm .getHours();

           

      if (local_hh >= work_start && local_hh <= work_end)

      {

          return true;

      }

      else

      {

          return false;

      } 

}

Avatar

Employee

Hi,

have you tried it in a javascript activity? It is simplier to debug.

Please try the following fix:

var resultSet = tempQuery.ExecuteQuery().recipient;

Best regards

Avatar

Level 3

The above Typology rule scripts works fine in Java Script activity.  I want the same script to work in Typology rules, so that I can reuse it in all the Email's.

Avatar

Correct answer by
Level 10

Hi,

Before I start, I'll admit that I've never used a Control typology rule in production before, but here is what I think is happening:

My understanding is that the script in the Control typology rule is run for each delivery it applies to. It is expected that the script will return true or false for each of those deliveries. This means that the Control rule is used to check the validity of the delivery, not details relating to the recipient. See the example here: Control rules.

I believe your script does not actually return true or false because the return statement is in a for block. This means that if there are no items to iterate over, your script will never return anything. This might be the case because I agree with marcoc31069762that you should not be iterating over resultSet but resultSet.recipient. Check out this documentation with examples: SOAP methods in JavaScript​. You can see that if you use operation="select" in your query your result set will contain a list of results stored in a sub-property.

Even if your for block does iterate more than 0 times, it will return true or false for the first recipient in your resultSet (not the recipient of the actual delivery in question) and then exit.

This would explain why your script works in a JS activity (it is valid Javascript), but does not work in a Control rule (since the rule expects true/false). Furthermore, there is a serious performance issue with this script because if I understood correctly, the query would be executed for each delivery. If you have 100k deliveries for example, I think this script would grind your instance to a halt.

Again, that is just my understanding, perhaps someone from staff can confirm or deny this theory

However, here are some general suggestions for your script:

  1. As a best-practice, you should use var to define your variables, otherwise Javascript will create and store them in the global scope. In your browser this is the window object. I am not expert enough to tell you what the global scope is in Campaign, but if you assign values without var, you risk overwriting existing important global properties.
  2. You can optimise this script by assigning local_hh without creating two more Date objects I made the change below.
  3. You can simply return the value of local_hh >= work_start && local_hh <= work_end without doing an if/else, I also made this change below.
  4. By convention, people typically use camelCasel to name variables and functions in Javascript. In your script you use a mix of camelCase, lowercase and snake_case. It makes your script a bit hard to read. I would suggest changing your variable names to camelCase.

var tempQuery = xtk.queryDef.create( < queryDef schema = "nms:recipient"

    operation = "select" > < select > < node expr = "@id" / > < node expr = "@timezone" / > < /select> <where> <condition expr={"@timezone is not null"}/ > < /where></queryDef > );

var resultSet = tempQuery.ExecuteQuery();

for each(var row in resultSet)

{

    var recipientId = row.@id;

    var recipientTz = row.@timezone;

    var today = new Date();

    var localoffset = -(today.getTimezoneOffset() / 60);

    if (recipientTz == "EST") {

        tzdestoffset = -4;

       

    } else if (recipientTz == "CST") {

        tzdestoffset = -5;

       

    } else if (recipientTz == "MST") {

        tzdestoffset = -6;

       

    } else if (recipientTz == "PST") {

        tzdestoffset = -7;

       

    } else {

        logInfo('TimeZone is missing');

    }

   

    var estoffset = tzdestoffset - localoffset;

    var local_hh = today.getHours() + estoffset;

    var local_dttm = new Date(new Date().getTime() + estoffset * 3600 * 1000);

    var local_hh = local_dttm.getHours();

    return local_hh >= work_start && local_hh <= work_end;

    if (local_hh >= work_start && local_hh <= work_end)

    {

        return true;

    } else

    {

        return false;

    }

}