Automation concepts – Using Created Variables as Numbers

Estimated time to read: 10 minutes

 

After repeatedly re-discovering what works (or does not) for text-to-number conversion in rules, I decided to pause and fully exercise the documented functions which could be used with a created variable as a number.  This article describes the results of my testing and findings as of the posting date.

 

TL; DR: Using an automation Create Variable action temporarily stores information for later use.  Those variables are string type values, and may be converted to other types such as date / times, lists of strings, JSON, and numbers.  When a number is needed, the conversion is not always simple.  Understanding how to convert a variable saves rule-writers time, frustration, and may help explain rule behaviors and errors not covered in the documentation.  When in doubt, experiment to test your rule.  Based upon my experimentation of the documented functions, here is a summary of what works, or does not, when converting a variable with the asNumber function. For example, {{varSomeVariable.asNumber}}.

That additional conversion step is to add the variable to a known number first, such as with:

{{issue.summary.rightPad(someZeroNumberField.plus(varMyNumber.asNumber), "!")}}

 

Why use variables?

Automation rule components and smart values are essentially a “little programming language”.  And as with other languages, storing temporary values helps to remember things needed later.  One example is saving a hard-coded value, such as template text, that will be used repeatedly in later rule steps.  Thus, the variable prevents duplication  / sync errors rather than copying the template text to different rule steps. 

The same applies for number values.  A good example is calculating a sum (or other value) for use in different places in the rule, such as conditions, emails, and messages.  And, using a saved variable might improve rule performance as the calculation only happens once with the result reused later.

Sounds great; so, what’s the problem?

 

A short list of automation gotchas and things to know

Like all programming languages, automation rules and smart values have limitations.  The feature is still relatively new (as compared to other Atlassian products), and automation continues to improve over time.  Let’s review a short list of some of the automation gotchas as they impact number use and created variables treated as numbers.

 

Documentation issues

As capabilities of rules improve, they sometimes get out of sync with the provided documentation.  And there are defects in smart value functions which are not documented, or perhaps unknown to Atlassian.  My hypothesis is some smart value documentation is based upon Java language sources and was perhaps not compared to the automation implementation behavior.  Rather than getting stuck when a rule does not work as the documentation shows, I recommend experimentation to confirm things work as you expect, and to respond accordingly when they do not.

 

Number rounding (stored versus displayed)

As with most languages, automation rules likely have a limit on the number of significant digits stored, impacting the precision of values.  (And some values likely cannot be precisely stored within computers at all, but that is beyond the scope of this article).  Thus, there can be rounding up / down of values, depending upon the math operation, leading to some stray decimal digits around the 15th or 16th digit.  Testing will show when a rule needs to handle this, such as with the Ceiling, Floor, or Round functions.

 

Number type handling

One of the things missing from documentation is the behavior of number types in different smart value expressions.  The types of interest are Integers and Floating-Point numbers; for example, 2 and 1.6

The long-format of math expressions automagically converts text to numbers, regardless if they are an Integer or a Floating-Point value.  Let’s look at this example set of rule steps, multiplying two such variables:

Simple math rule, COMBINED - Copy.png

Simple math rule, audit log results - Copy.png

In the first audit log write (with Log action), the long-format correctly converts the values, usually returning exactly 3.2.  (Please see number rounding information above.)

But inline math functions appear to use value typing from left-to-right.  As a result, when the Integer value is first, the value is truncated and returns 3.  When the Floating-point value is first, the result is the expected 3.2 value.  This left-to-right evaluation behavior is quite important, and we can leverage it later in the article to help smart value functions that would not otherwise work with created variables.

I have read several community questions about this symptom, where people used the inline divide() function and were surprised by the results, such as when calculating the percentage of completed issues from a Lookup Issues action and always getting a zero result.  Some have even reported this as a defect to Atlassian.  As shown in this rule example above, one possible solution is to use the long-format math expression to better manage the types and precision needed.

 

Now we understand the needs and some possible challenges.  Let’s get to the behaviors for the different types of functions and any workarounds to help!

 

Math functions

With long-format math expressions, conversion is rarely needed.  Most of inline math functions also work as expected with the asNumber function.  For some examples:

{{varMyNegativeNumber.asNumber.abs}}

{{varMyFloatingPointNumber.asNumber.round}}

{{varMyFloatingPoint.asNumber.multiply(varMyInteger.asNumber)}}

{{varMyValue.asNumber.gte(varMyOtherValue.asNumber)}}

The possible rounding errors and behavior with Integer and Floating-point value ordering were noted earlier, along with suggested workarounds.  I did observe an additional rounding symptom where non-terminating, repeating decimals (e.g., 1.45454545454545…) appeared to inconsistently round (although I suspect that may not impact many customers).

 

Additionally, and unrelated to the use of variables, the following functions did not work as documented.  I recommend extensive testing if you try to use these:

  • formatWithLocale(input) – returns no value
  • asPercentage(locale) – appears to return additional non-breaking spacing
  • asCurrency(locale) – appears to return additional non-breaking spacing

 

Date / time functions

Most of inline date / time functions work as expected with the asNumber function.  For some examples:

{{issue.duedate.plusDays(varMyDays.asNumber)}}

{{now.toStartOfDay.withHour(varMyHour.asNumber).minusDays(varMyDays.asNumber)}}

{{now.ofTheMonth(varMyNthDay.asNumber, varMyDayOfWeek.asNumber)}}

 

Additionally, and unrelated to the use of variables, the following functions did not work as documented, returning no values at this time:

  • withDayOfWeek(input)
  • withWeekOfYear(input)
  • withWeekOfYearIso(input)

There is a workaround for the withDayOfWeek() function to use the long-format for a result, and that works with variables converted to numbers:

{{#now}}func=withDayOfWeek({{varMyDay.asNumber}}){{/}}

 

List functions

There are two list functions which can take a number parameter, and neither works directly with created variables: get() and getFromEnd().  This is where the additional conversion is needed to help the function accept an Integer parameter.

One way to do this is to add and then subtract from a number value, finally using the math function plus() to add the variable’s value (as we already know plus() works with created variable, numbers).  But what if we do not have an issue field to use as a starting point?

What we need is something that always evaluates to 0 as an integer.  This is where {{null}} can help, as it is…well…null.  The approach is:

  • Create a variable named varNull which has a value of {{null}}
  • The text function length() returns an Integer, and a null value string has a length of 0 (This is in automation rules; outside of rules the value would likely be undefined.)
  • We may add any number variable to that zero-length, such as with this:
{{varNull.length().plus(varMyNumber.asNumber)}}

 

Let’s look at an example rule, where we use Lookup Issues to gather some issues, find the middle one, and return its key.  Once we have identified the middle point’s index, we can use the above expression with the list get() function.

Simple list rule, COMBINED - Copy.png

Simple list rule, audit log results - Copy.png

Here is the expression to allow using the variable as a number for the get() function:

{{lookupIssues.get(varNull.length().plus(varMiddleIndex.asNumber)).key}}

This same approach works with the getFromEnd() function.

 

Text functions

The only text function which works directly with variables containing numbers is the obvious one: isNumeric().  But it only returns true for whole numbers, and not for negative or floating-point values.

{{varPaddingSize.isNumeric}}

None of the other text functions will work directly with a variable and the asNumber conversion.  Instead, the same technique noted above for list functions will work.  For example, to right pad an issue's Summary to a dynamic number of characters, this would work:

{{issue.summary.rightPad(varNull.length().plus(varPaddingSize.asNumber), "!")}}

The same technique works for the other text functions.

 

Wrapping it up…

If you have read all the way to here, well done!

Whew!  That was a lot of detail to get to the workaround with the null variable…and I believe the context was needed to help clarify it.  In my opinion, successfully using automation rules requires learning and experimentation.  Hopefully you learned some automation tips beyond just the workaround, and saw how one might experiment to identify and the resolve limitations in the tools.

 

Happy rule writing!

 

0 comments

Comment

Log in or Sign up to comment
TAGS
AUG Leaders

Atlassian Community Events