Example 1 - Calling up exchange rates
Description of the scenario
This example describes how you can retrieve exchange rates with a REST call action and process them further in Intrexx.
The retrieval of exchange rates is triggered by clicking on the "Get rates" button in the "Exchange rates" application. The prices are then displayed. Each time you click again, the exchange rates are retrieved again. This is done for the US dollar (USD), British pound (GBP), Japanese yen (JPY) and Chinese renminbi yuan (CNY), for example.
The exchange rates are written to a data group after each click. The data group contains one column per currency, each of which contains the current exchange rate.
The application can then make the requested rates available to other applications in the portal, for example for invoicing.
German customs
In the example, the courses are provided by German Customs via a REST API. It publishes daily exchange rates that are used as a basis for the valuation of goods.
The URL of the page is: www.zoll.de/SiteGlobals/Functions/Kurse/App/KursExport.txt?view=jsonexportkurseZOLLWeb
Description of the response data
The data is provided as Json. They have the following form:
{
"kurse":[
{
"kurswert":50.96635,
"iso3":"EGP",
"name":"Ägyptisches Pfund"
},
.....
{
"kurswert":10.8847,
"iso3":"MAD",
"name":"Marokkanischer Dirham"
},
.....
{
"kurswert":1.0638,
"iso3":"USD",
"name":"US-Dollar"
}
]
}
The Json object contains an array with the name "kurse", in which a Json object is embedded for each currency. Each of these objects contains the following three fields:
-
"market value", i.e. the exchange rate
-
"iso3", the ISO designation of the currency
-
"name", the full name of the currency
Process response data
In principle, a REST call makes all received data available in the processing context (SharedState) of a process (workflow). However, the respective REST APIs deliver the data differently, so that Intrexx cannot usually process them automatically without further ado. This means that the data received must first be prepared using a Groovy action before it can be processed further.
Create application and process
The following describes how to create the application and the process with REST call action. The central steps are described and illustrated using screenshots.
Application structure
To be able to save the rates, the application requires a data group in which four columns are created for the four currencies. These columns are created as floating point numbers so that the incoming decimal places can be stored correctly.
The display is an overview page with a view table in which the four columns plus the time stamp of the last change are displayed. As the process to be created is to update the data in the table, an entry must already exist at the start, which can then be updated. To do this, you can use the page wizard to create a simple entry page for all four currencies and create an entry with any values. These are then simply overwritten with the first REST call.
Structure of the process
Trigger process ()
The process should be triggered by clicking on the "Get courses" button in the application. A generic event handler can be used for this purpose, which uses a UserWorkflowEventHandler as the event handler. This handler can be notified from the portal with the JavaScript command triggerUserWorkflowEvent() and thus trigger the process (see below). When the handler is entered, a GUID is generated which is required later to notify the handler.
Set up REST call ()
General The event handler now triggers a REST call that retrieves the exchange rates. In the example, the call is called"getExchangeRates". The results can be accessed later in the processing context under this alias. As a Json is delivered by "zoll.de", the "Parse JSON" checkbox is activated. This converts the supplied text into a Json object, which can then be edited further down the line.
Authentication and header As the page does not require any special authentication, no further settings are necessary for "Authentication and header".
Request A GET request is required to retrieve the Json. This goes to the host zoll.de and there to the path SiteGlobals/Functions/Courses/App/CourseExport.txt (the part of the address up to the "?", which indicates the start of the query parameters). The output as Json is controlled by setting the query parameter "view" to the path"jsonexportkurseZOLLWeb". This can be entered directly in the list of query parameters.
Body As no data needs to be provided for the call, no payload is required in the "Body" tab.
Extract exchange rates ()
This process step involves processing the json received and placing selected exchange rates in the processing context. A Groovy script action is used for this. The exchange rates are extracted using the "JsonPath library".
The call stores the received json with the exchange rates in the processing context under its alias as getExchangeRates.body.json (the raw text is available under getExchangeRates.body.text ). To extract the individual values, you can use the JsonPath library, for example, which is used to access previously placed rest calls. It contains an object JsonPath, which is made available via the line import com.jayway.jsonpath.JsonPath;.
The structure of a Json path is only outlined here. A detailed introduction can be found on the library's website at https://github.com/json-path/JsonPath or tutorials such as https://www.baeldung.com/guide-to-jayway-jsonpath. There are also pages on the Internet that evaluate JsonPaths and check whether they are correct (e.g. https://jsonpath. com/).
A Json path basically consists of a path through the hierarchically structured Json object. The symbol "$" stands for the entire json at the beginning, the individual steps are given the name of the respective element and are separated by a dot. In arrays, one or more elements can be selected using square brackets. JsonPaths also offer the option of checking predicates via "?()" and thus selecting elements that meet certain conditions. The elements are then designated with "@" in the query in order to reach their subordinate elements.
In our case, to select a course from the json ($), we need the array "kurse" and the element that contains the ISO code we are looking for in its sub-element "iso3". Once we have found the element, we need its component "price value".
This results in the path "$.kurse[].kurswert", whereby the currency being searched for must be entered in the square brackets. So first "?()" to formulate a query, in whose brackets the respective element of "kurse" is then designated with "@", so that "@.iso3" means "the field "iso3" in the current element". If this corresponds to the identifier of the desired currency, e.g. USD ("@.iso3 == 'USD'), it is selected. Our predicate is therefore "?(@.iso3 == 'USD')" for US dollars. When inserted into the path, the result is
$.kurse[?(@.iso3 == 'USD')].kurswert to achieve the exchange rate for US dollars.
The JsonPath object provides a read() method that takes the json to be examined and the json path as a string. As "$" is used as a placeholder in Groovy, it must be masked with a backslash. As the return of the REST call is in the processing context, it (and therefore also the json) can be accessed via the g_sharedState object: g_sharedState.getExchangeRates.body.json
The call can then be made as follows:
JsonPath.read(g_sharedState.getExchangeRates.body.json, "\$.rate[?(@.iso3 == 'USD')].rate value")[0]
The JsonPath object attempts to determine a suitable return and in this case offers an array with only one value (the exchange rate). However, as the array itself is not required, but only the value in it, it can be accessed with [0] for the first value in accordance with the usual counting from 0.
The result can then be saved in the processing context. The same dot notation can be used for this as for reading: g_sharedState.usd thus creates a place where the exchange rate for USD can be saved in the processing context. It can then be called up under the name "usd". The same procedure can be used for the other values:
g_sharedState.usd = JsonPath.read(g_sharedState.getExchangeRates.body.json, "\$.kurse[?(@.iso3 == 'USD')].kurswert")[0];
Below you will find the Groovy script with the information on the Json path.
// JsonPath-Bibliothek importieren, um Daten aus dem Json im Verarbeitungskontext zu extrahieren
import com.jayway.jsonpath.JsonPath;
// Wechselkurse anhand des ISO3-Codes aus dem Json wählen und im Verarbeitungskontext ablegen
//
// Struktur des Json-Pfads:
// $.kurse[?(@.iso3 == 'GBP')].kurswert
// ^ ^ ^ ^
// | | | Darin das interessierende Feld "kurswert"
// | | Abfrage: im Objekt aus der Liste "kurse" soll der Eintrag bei "iso3" dem Wert "GPB" entsprechen
// | Referenz auf das untergeordnete "kurse"-Objekt
// Referenz auf das Json-Objekt
//
// Anmerkung: Im String für JsonPath.read() muss $ mit \ maskiert werden, da es
// in Groovy sonst als Platzhalter interpretiert wird
//
// Die JsonPath.read()-Abfrage liefert ein Array mit nur einem Eintrag zurück (dem Kurswert), dieser wird mit [0] erfasst
// Im Verarbeitungskontext (shared state) werden vier Objekte angelegt für USD, GBP, JPY und CNY
g_sharedState.usd = JsonPath.read(g_sharedState.getExchangeRates.body.json, "\$.kurse[?(@.iso3 == 'USD')].kurswert")[0];
g_sharedState.gbp = JsonPath.read(g_sharedState.getExchangeRates.body.json, "\$.kurse[?(@.iso3 == 'GBP')].kurswert")[0];
g_sharedState.jpy = JsonPath.read(g_sharedState.getExchangeRates.body.json, "\$.kurse[?(@.iso3 == 'JPY')].kurswert")[0];
g_sharedState.cny = JsonPath.read(g_sharedState.getExchangeRates.body.json, "\$.kurse[?(@.iso3 == 'CNY')].kurswert")[0];
Enter values in a data group ()
In the final step, the four values in the processing context should be saved in the application's data group. This can be implemented with a data group action that can enter values in a data group.
As the data group of the application only contains one line, it is not necessary to use a filter to determine which line the data should be written to. Instead, it is sufficient to select under "General" that the action should change a data record (under "Add", a new line would be inserted for each call instead).
The data group of the application can then be selected under "Target data group".
The "Manipulation set" tab contains information on which rows of the data group should contain the new values. As we only have one line in the data set, it is sufficient not to set a filter here. The change is then applied to all rows in the data group.
The field assignment determines which values are entered in which columns.
A user-defined value can be created as the source in the right-hand window.
Under "System value", a value can be retrieved from the processing context, the name of which (here: the names of the currency from the Groovy script) must still be specified.
After assignment to the target columns (left-hand window), the values are updated in the data group each time the process is executed.
Connecting application and process
In order to trigger the process by clicking on the "Get courses" button, the button is linked to a Javascript function.
In the properties of the button, the call for a JavaScript function is stored in the "Script" tab, which we call "execute()".
This function can then be inserted in the Javascript editor.
The "execute()" function calls triggerUserWorkflowEvent with the GUID of the event handler and registers two functions that are executed when the process has been successfully completed or an error has been reported. As the page should refresh after clicking on the button and display the new prices, location.reload() can be called here if successful.
Below you will find the JavaScript code.
function execute() {
$.when(triggerUserWorkflowEvent('40AD0EE848B0B4765FE94A2E67E38C9DCBA8D109'))
.done(function()
{
// Bei erfolgreicher Workflow-Ausführung: Seite neu laden
location.reload();
}
)
.fail(function()
{
// Bei fehlerhafter Workflow-Ausführung: Meldung und ggf. Fehlerbehandlung
alert("Fehler bei der Ausführung.");
}
);
return true;
}