Skip to main content

Hello,

I have a question about the use of deep linking to Assyst resources when data mapping in ETM. I’d love some feedback on my current approach,  and whether there are other approaches I haven’t considered.

 

TL;DR

  • Is it possible in ETM to refer to the AssystREST URL that is specified in the IPaaS/ETM installation, as to create deep linking to the same Assyst environment ETM is attached to?
  • Are there other ways to do deep linking in Assyst ETM?

 

Background

Our IT users spend a lot of time trying to find the correct menu for a given resource and waiting through load times during manual lookups. Because of this, direct links to resources are almost always preferred to use speed up the process.

When working on integrations in Assyst ETM, I thus often find it helpful to add deep links to Assyst resources (e.g., specific actions, events, items), which would normally only be added as plain text shortCode during manual processes, or simple integrations.

These URLs can be added as clickable links in formatted fields or as easy-to-copy plain text, but both methods provide value to our IT users as it reduces friction, and end up saving time. The use of these links also introduces resource IDs to the updated records, which helps reduce ambiguity in review or audit situations where a resource since might have been deleted and a new one has been created with the same shortCode.

However, the deep links often end up pointing to the wrong environment during development/testing, and has a real danger of pointing to the wrong environment when in production as they must be manually updated before import into ETM.

 

What I’ve Tried

So far, I've been copying known assystWeb/AssystNet URLs into the data mapper to act as templates, appending the resource ID to a hardcoded base URL like:

"https://assyst-dev.our-domain.net/assystweb/application.do#item/ManageItem.do?dispatch=get&id=" + variables.MappedItem

Or, when using multiple data mappers in a channel, it makes sense to use an initialization data mapper to set the environment once and refer to it throughout that channel:

"https://assyst-" + mappedp0].variables.environment + ".our-domain.net/assystweb/application.do#item/ManageItem.do?dispatch=get&id=" + variables.MappedItem

This works mostly fine when working on a few channels. However, it increasingly becomes a pain as more integrations are developed. The manual task of updating links when moving between ETM environments (e.g., dev → test → staging → prod) adds friction. Every instance of the static environment reference must be manually updated, which is error-prone and frustrating, especially if such a channel producing deep links to a different environment ends up in production.

 

What I'm Trying to Achieve

Ideally I’d there would be a simple way to generate clickable links that point to the correct Asstst application and enviroemnt, however i find it alot more realistic there might be a way to reference the AssystREST base URL that is configured in the ETM/IPaaS installation as ETM should have this failly easily accessible. Using this URL a base assyst url for the attached Assyst environment could be made, and furhter use in datamapping to construct any desired application deeplink, thus circumventing the need for manuel edits between import and exports.

However, I haven’t found any way to access the AssystREST URL directly from within data mapping.

What I've Tried So Far

  • Creating an initializing data mapper to hold the base URL,  but this still requires manual updates between each environment for expedient testing and validation, and this method is prone to human error.
  • Inspecting the headers object in data mapping to look for any reference to the AssystREST URL does not seem to have any header that reference the assyst application or rest api urls, at least as of IPaaS 1.8.

Looking for Ideas

  • Is there a way to reference the AssystREST base URL dynamically from within ETM?
  • Has anyone tried or know if it might it be possible to use Karaf environment variables or other IPaaS config-driven approaches to solve this?

Thanks in advance for any insights or experiences you can share!

Some thoughts:
If you are using clickable links in html fields, can you use <a> element with relative rather than absolute paths?

Use an ETM lookup table to store the base url. This can be shared by all mappers & channels - so is just one thing to change.

Store the URL on some arbitrary record in assyst. e.g. a Company or something. Retrieve the URL from the Company in the mapper - you’d need to remember to update the Company after a database copy - so not much better than a Lookuptable (arguable worse)


Building on Paul’s idea about arbitrary values in the system to help drive this. It should require zero effort once set up. Fingers crossed I haven't over-engineered this one.

As a cloud customer we have IPAAS accounts named ddatabaseName-ASSYSTIPAAS] which means each environment has a unique account when taking actions in the system. This post assumes (an replied on) you have similar segregation in place.

The entity:

Create an item (for example) in each environment with the same shortCode. Example below has also been CSG’ed away to avoid clutter.

The ETM:

Set up a scheduled ETM channel in each environment that will force update this item. This will ensure the item’s ‘modifyId’ value is always the account unique to this environment. Using the account and not the entity ensures the same mapper channel will produce different results reducing effort and reducing human error chances when replicating environments.

The Lookup Table:


Calling the Value:

I have obviously forced this to display what I want so remove line 5: 

The Lookup:

item = common.search("item", { "shortCode": "ASSYSTENVIRON" }, T], false)}0];
// for web - Delete as required
item.modifyId;
// for net - Delete as required
item.modifyId + "-NET";

The Variable:

base = variables.url;
// for web - Delete as required
eventId = variables._eventi0].id;
// for net - Delete as required
eventId = variables._eventi0].formattedReference;

finalUrl = base + "/application.do#item/ManageItem.do?dispatch=get&id=" + eventId;

finalUrl;

You can of course "/application.do#item/ManageItem.do?dispatch=get&id=" in the lookup table value, I just did it this way out of preference.

 

Not tested btw, so make sure you do so before using this yourselves.


Thank you very much ​@Paul McCulloch and ​@Steve Miller, you have both given me great ideas on how to proceed with this.

We had not been using lookup tables, and it had not occurred to me that they would be the tool for the job 😝.

 

I very much like the elegance of the method you propose ​Steve, but we have a local installation where the assyst user, as of now, does not follow such a neat pattern where the name can be used to derive the Assyst/IPaaS environment.

However, your idea, combined with ​Paul’s, led me to the idea of creating a more general-purpose lookup table, combined with using several lookup table variables to build a more general URL constructor during data mapping.

Example lookup table for resource and environment. “Current” is here used as a default so that imported channels which also by default has that in the datamappers will point to that IPaaS environments assyst installation. 

My current idea is the use of a general lookup table for environment, base URL, resource,  endpoint etc and then use these in two Variable lookups + a variable that constructs the final url.
 

Ignore the duplicate lookup variable name for url part 2, it's just there to get the field order to work in my favor to display the result of both lookup versions below each field.

As can be seen in the example above, two variable lookups are used to construct the two parts of the URL alongside the desired resource ID. With some minor modifications to the URL_part2 could have an variable as its own input and then be fully dynamic based on a variable assyst search etc. 

I’ve also made an attempt at using a named function at the end, which while not pretty does the job for illustrating the idea of the url constuction with validated inputs:  

function buildAssystUrl(envPart, resourcePart, resourceId, importChannelName, importReference) {
// Validate inputs
if (!envPart || typeof envPart !== "string") {
logger.error("ETM " + importChannelName + ", " + importReference + " could not generate a link during datamapping: Missing or invalid environment part.");
return "";
}

if (!resourcePart || typeof resourcePart !== "string") {
logger.error("ETM " + importChannelName + ", " + importReference + " could not generate a link during datamapping: Missing or invalid resource part.");
return "";
}

if (typeof resourceId !== "number" || isNaN(resourceId)) {
logger.error("ETM " + importChannelName + ", " + importReference + " could not generate a link during datamapping: Missing or invalid resource ID.");
return "";
}

// Normalize envPart: remove trailing slash if present
if (envPart.charAt(envPart.length - 1) === "/") {
envPart = envPart.substring(0, envPart.length - 1);
}

// Normalize resourcePart: remove leading slash if present
if (resourcePart.charAt(0) === "/") {
resourcePart = resourcePart.substring(1);
}

// Start building the URL
var url = envPart + "/" + resourcePart;

// Append ID depending on whether resourcePart already contains a query string
if (resourcePart.indexOf("?") === -1) {
// Path-style: e.g., #items/602
if (url.charAt(url.length - 1) !== "/") {
url += "/";
}
url += resourceId;
} else {
// Query-style: e.g., ?dispatch=get&id=602
if (url.indexOf("id=") === -1) {
url += "&id=" + resourceId;
} else {
logger.error("ETM " + importChannelName + ", " + importReference + " could not generate a link: resourcePart already contains an ID.");
return "";
}
}

return url;
}


var envPart = variables.Deeplink_URL_part_1;
var resourcePart = variables.Deeplink_URL_part_2;
var resourceId = variables.AffectedItemu0].id;

// Defaults set for more consistent results when using debug with sample data.
var importChannelName = headersv"ChannelConfigName"] || "UnknownChannel";
var importReference = headers/"ProcessedResourceIdentifier"] || "UnknownReference";

buildAssystUrl(envPart, resourcePart, resourceId, importChannelName, importReference);

The main issue with this whole approach is the verbosity of it all and having to add these fields multiple times across datamappers, the named function especially so.


This should be addressable if making use of a named function in an initialization datamapper, and in later data mappers call upon this, which according to the wiki seems to be possible(?). However, I’ve been unable to use functions defined in one data mapper field in a later data mapper field, let alone in a later data mapper 😅.

If anyone has any examples of how to define a named function in one Data Mapper and use it in a following one, that would be very helpful.

Again, thank you very much for your help!


Functions need to be anonymous to be reused. 

So define a mapper variable with name buildAssystUrl. The value of the expression would be


function (envPart, resourcePart, resourceId, importChannelName, importReference) {

...

return url;

}


i.e. a function with no name.

To call the function in the same mapper:

variables.buildAssystUrl(envPart, resourcePart, resourceId, importChannelName, importReference);

 

from another mapper

mappedp0].variables.buildAssystUrl(envPart, resourcePart, resourceId, importChannelName, importReference);

or

mappedByNamea‘My Shared Mapper’].variables.buildAssystUrl(envPart, resourcePart, resourceId, importChannelName, importReference);


Reply