Best practices for handling array rule inputs that aren't true null when passed a singleton of the same type, especially in regards to query entity rules?

Certified Senior Developer

Hi all,

 

Wanted to ask the community to see if anyone has some best practice recommendations to handle the edge cases caused by Appian limitations in how it detects null/empty values and how it converts null singleton types into array rule inputs; and especially recommendations on setting up re-usable query entity rules.

 

We've discovered that Appian handles nulls in unintuitive ways depending on the data-type. If a rule input is an array, and you pass in a singleton of the same type that is null, Appian will automatically convert that input to {null} (a one element array where that element is null), rather than null or {} (empty). According to Appian support, this is the expected behavior of the product.

 

However, this causes two issues for us:

 

 

  1. The out-of-the-box isnull() and a!isNullOrEmpty() functions return false, despite a reasonable assumption that the value is null/empty. (It is not. It is a one element array with that element being null, due to how the product casts singletons to arrays.)

 

A work-around here is to instead use a common rule that includes a length(ri!input)=0 check, as in: or(a!isNullOrEmpty(ri!input), length(ri!input) = 0 ). The downside here is that it is very easy for developers to miss using this.

 

  1. If the rule input is used in an a!queryEntity() filter, "ignoreFiltersWithEmptyValues" will not ignore this filter, despite a null being passed into the rule input (again because it is automatically converted to something that is not null or empty).

 

We frequently develop expression rules for re-usable queries and prefer to use array inputs so that we can optionally use an "IN" filter, even in cases where we usually only pass in a single value to that filter. However, this is causing failures when we pass a null value in. We've found a few possible work-arounds, but there are drawbacks to each:

 

  1. When passing the single input that might be null, wrap it in a!defaultValue(ri!input, null). If it's null, it will replace it with a typeless null, which correctly gets converted to null of the array type and works as expected. The downside of this is that it places the burden on the rule-caller to ensure this is done, and this is easily missed (and it makes calling the rule non-intuitive).
  2. Update our a!queryEntity rules to add applyWhen: not(length(ri!filterValue)=0), since we can't rely on "ignoreFiltersWithEmptyValues" to catch this edge case. The downside here is that we've already constructed over a thousand query entity rules across multiple apps, so this will be time consuming to implement; and it's also non-intuitive and easily missed by developers.
  3. Duplicate all the inputs that take arrays to add a variation that only takes the singleton version. The downside here is this will greatly increase the number of inputs, and can cause a lot of confusion, and the tech-debt to implement this on existing queries is near insurmountable. This could only be done for brand new queries, but it feels like an ugly hack.

 

 

In all these work-around options, there is a major downside that it is very easy for developers to miss this edge case, even if we make it a best practice to always use the work-around. Because it's a rare edge case and seldom causes errors to be thrown, defects can easily go undetected for a very long time, and become challenging to trouble-shoot. The only way to detect that these work-arounds weren't applied is with a manual code review.

 

I was wondering if others have encountered these use cases and implemented any design principles or best practices to avoid this pitfall. Even if we have to use some of the above work-arounds on existing tech-debt, we'd like to set a new best practice for designs going forward, especially for query rule development.

 

Thanks!

  Discussion posts and replies are publicly visible

Parents Reply Children