Passthrough Blocks in Call URL and Navigate Actions

TeamDesk provides 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 the start of the query string, and '&' to delimit query parameters. XML uses '<' and '>' to denote tags, and 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 an XML document? No way. But you should escape them; in other words, decorate them somehow.

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

Automatic Encoding in Call URL Action

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

But sometimes automatic encoding poses a problem. Suppose your Call URL action calls the service with a URL that comes from the database. An attempt to use a sort of <%[Base URL]%>/path/to/api will lead to an 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, the easiest way to generate the API URL of the database is to use

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

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

Using Passthrough Blocks for Call URL Action Body

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

Suppose you are sending an invoice to a third-party system. The 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, a JSON representation of the item using a regular text formula, for example:

"{" & List(",",
    '"product":' & JSONFormat([Product]),
    '"quantity":' & JSONFormat([Quantity]),
    '"price":' & JSONFormat([Price]),
    '"total":' & JSONFormat([Total])
) & "}"

Then concatenate individual items’ JSON via a summary column, but what’s then? Using the summary directly in the 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 the 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 the master record has, the more complicated it gets. Also, the Text body type serves as "do whatever you want," while Form/XML/JSON types control the overall validity of the text according to a format. Missing brackets in JSON, unclosed tags in XML won’t be detected with the Text body type, turning the action’s setup into a set of trial and error attempts. Edit, save, test, edit, save, test…

And the solution is … Passthrough Blocks

Just to help deal 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]%>
}