Passthrough blocks in Call URL and Navigate actions

TeamDesk providers a way to suppress automatic data encoding in Call URL and Navigate actions using <%* expression %> passthrough blocks.

Text-based formats, like URLs, XML or JSON add special meaning to some characters. For example, URLs reserve ‘/’ as a path separator, ‘?’ to denote the end of the path and start of the query string and ‘&’ to delimit query parameters. XML uses ‘<‘ and ‘>’ to denote tags. JSON surrounds string values with quotes. Does it mean you cannot put question marks in a value of query parameter of URLs or angle brackets in the text inside XML document? No way. But you should escape them; in other words, decorate them somehow.

URLs use percent encoding to specify character via its ASCII code; XML uses entity references to escape special characters, JSON uses backslash prefix to suppress any special meaning of the character that follows.

When using text formulas in TeamDesk you could call URLEncode(text), XMLEncode(text) or JSONEncode(text) functions to perform format specific escapes.

The problem

Call URL action’s URL and Body parts usually built using a lot of columns’ data and wrapping every column with encode function is a bit tedious job; so we introduced special <% expression %> syntax where expression gets encoded automatically. Always.

But sometimes automatic encoding poses the problem. Suppose your Call URL action calls the service with an URL that comes from the database. An attempt to use a sort of <%[Base URL]%>/path/to/api will lead to error any slashes in [Base URLs] value will be encoded. So, oh, we should not encode URLs. Well, we detect URL columns and formulas, URL(), URLRoot() and BackURL() functions and do not run automatic encoding. But what if you need to calculate the value derived from those functions? For example, easiest way to generate API URL of the database is to use

Replace(URLRoot(), "/db/", "/api/v2/")

But once Replace() or other function kicks in we can no longer suppose the result is the URL.

Another example

There is another, fairly common use case, when Call URLs action body is built with master record’s data with an addition of set detail records.

Suppose you are sending invoice to third-party system. Invoice contains header fields, such as Contact, Company, Billing and Shipping Addresses and related items. For example:

{

"contact": <%=[Contact]%>,

"billingAddress": <%=[Billing Address]%>

...

"items": [ /* something there for items */ ],

"total": <%=[Total]%>

}

At first glance it seems simple enough. TeamDesk lacks looping capabilities but you can build, say, JSON representation of the item using regular text formula, for example:

"{" & List(",",

'"product":' & JSONFormat([Product]),

'"quantity":' & JSONFormat([Quantity]),

'"price":' & JSONFormat([Price]),

'"total":' & JSONFormat([Total])

) & "}"

then concatenate individual items’ JSON via summary column, but what’s then? Using summary directly in JSON body won’t work — its content will be escaped.

The only way to deal with this is to avoid escaping at all, and escape manually when needed. To do it you can set Body type to Text and wrap every invoice header field with JSONEncode() or JSONFormat().

{

"contact": <%JSONFormat([Contact])%>,

"billingAddress": <%JSONFormat([Billing Address])%>

...

"items": [ <%[Items]%> ],

"total": <%JSONFormat([Total])%>

}

But the more fields master record has, the more complicated it gets. Also, Text body type serves as “do whatever you want”, while Form/XML/JSON types control overall validity of the text according to a format. Missing brackets in JSON, unclosed tags in XML won’t be detected with Text body type turning action’s setup to a set of trial and error attempts. Edit, save, test, edit, save, test…

And the solution is … Passthrough blocks

Just to help dealing with scenarios we described, we are introducing passthrough blocks: <%* … %>. Their only purpose is to suppress automatic escaping where it is really needed without resorting to hacks. Need an API URL? Write:

<%* Replace(URLRoot(),"/db/","/api/v2/") %>

And here is the invoice body:

{

"contact": <%=[Contact]%>,

"billingAddress": <%=[Billing Address]%>

...

"items": [ <%*[Items]%> ],

"total": <%=[Total]%>

}

Next: Database