# Script API

## Input data retrieval <a href="#skrypty-scriptservice-pobieraniedanychwejsciowych" id="skrypty-scriptservice-pobieraniedanychwejsciowych"></a>

Below is sample code that shows how to use input and how to return data.

```javascript
function callService(context){
  const firstArgument = context.getFirstParameter("firstArgument");
  const firstArgumentList = context.getParameters("firstArgument");
  const keySet = context.getInputParameters().keySet();
  const comboboxLabel = context.getData("GesCombobox1","label");
  const result = "service script constant " + firstArgument + " list value: " + firstArgumentList + " and first from list: " + firstArgumentList.get(0) + " keySet " + keySet;
  return [{'output': result}];
}
```

## Input to Service Script <a href="#skrypty-scriptservice-wejsciedoskryptserwisu" id="skrypty-scriptservice-wejsciedoskryptserwisu"></a>

As input (parameter *context* in the code above) there is an object that provides the following methods:

* List\<String> **getParameters**(final String name); - retrieves a specific list from the input data map,
* String **getFirstParameter**(final String name); - retrieves the first element from a specific list from the input data map or returns the value "null",
* String **getFirstParameter**(final String name, final String defaultIfEmpty); - retrieves the first element from a specific list from the input data map, and if it does not exist or is empty, returns the value "defaultIfEmpty",
* Map\<String, List\<String>> **getInputParameters**(); - returns the entire input data map it stores,
* String **isVisible**(final String id); - retrieves information whether the component with the specified id is visible,
* String **getValue**(final String id); - retrieves the value of the component (also session variable) with the specified id,
* void **setValue**(final String componentId, final String value); - sets the value of the **value** component with the specified **componentId**. The rule for providing componentId is the same as for getValue.\
  When calling the script inside a composite component, in order to set the value of a component/session variable embedded in the same component, componentId must be prefixed with the character **@**

> **NOTE!** Attempting to execute **setValue** on a component that listens to another component or on a component **to** that is being listened to by other components will result in an exception being thrown.

* String **getData**(final String id, final String attribute); - retrieves the value of a specific attribute for the component with the specified id

> **NOTE!** The available attributes depend on the component.

* List\<Map\<String, String>> **callService**(String name, Map\<String, List\<String>> input); - enables calling ServiceProxy,
* ValidatorOutput **validate**(String name, Map\<String, List\<String>> input); - enables calling the validator

## Output from Service Script <a href="#skrypty-scriptservice-wyjsciezeskryptserwisu" id="skrypty-scriptservice-wyjsciezeskryptserwisu"></a>

The result of script execution can be returned in two ways:

1. **List of maps** - the output array of objects is parsed into a list of maps, and then processed in the standard way.
2. **Single map** - the return value is wrapped as a single-element list, and then processed in the standard way.

\
Output from the script with a sample list:

```javascript
  return [{'output': 'result1'},{'output': 'result2'},{'output': 'result3'}];
```

## Logging <a href="#skrypty-scriptservice-logowanielogowanie" id="skrypty-scriptservice-logowanielogowanie"></a>

Input data logging is automatic

Manual logging should follow the description in [Logging in ScriptCode](https://docs.eximee.com/documentation/documentation-en/budowanie-aplikacji/logika-biznesowa/scriptcode/logowanie-w-scriptcode)

```javascript
function callService(context) {
    const sessionVariable = context.getValue("@sessionVar");
    Logger.info('Session variable value [arg1={}, arg2={}]', nonsensitive('arg1 nonsensitive test'), sessionVariable);
    return [{'sessionVar':sessionVariable }];
}
```

## Throwing business exceptions

Business errors have been described in detail on the page [Error pages](https://docs.eximee.com/documentation/documentation-en/interfejs-uzytkownika/formularze/tworzenie-formularza/strony-bledow#bledy-biznesowe).

## Temporary data storage <a href="#skrypty-scriptservice-zapisdanychtymczasowych" id="skrypty-scriptservice-zapisdanychtymczasowych"></a>

It is possible to save certain data between service calls. This is done using the object `registry`.

Possible methods that can be called on the object `registry`:

* save(namespace, id, data) - save data
* saveVersion(namespace, id, data, version) - save data with version verification. If the version field is not set or is empty - save without verification. If the value of the version field does not match the current version of the stored data, the request will end with an error.
* get(namespace, id) - retrieve data
* getVersion(namespace, id) - retrieve the object { data: 'saved data', version: 'current data version' }
* delete(namespace, id) - delete data.

Data is stored in namespaces (namespace in methods) determined by administrators. Each namespace can be assigned a data lifetime. By default, it is **90 days**. Providing the same id in several save calls causes the data associated with that id to be updated. The data version is a unique identifier generated each time the data is saved.

{% hint style="info" %}
For correct operation, the object requires a working EximeeRegistry application.
{% endhint %}

{% hint style="danger" %}
**Saving data with EximeeRegistry is not intended for storing anything permanently!**
{% endhint %}

**Examples:**

```javascript
context.registry().save('test', '123123', 'temporary data');
context.registry().get('test', '123123'); // will return 'temporary data'
const item = context.registry().getVersion('test', '123123'); // will return the object {data,version}
context.registry().saveVersion('test','123123', 'new data', item.version); // matching version, save will succeed
context.registry().saveVersion('test','123123', 'new data', 'some other version'); // mismatched version, save will fail
context.registry().delete('test','123123');

```

## Retrieving the logged-in user's context <a href="#skrypty-scriptservice-pobieraniekontekstuzalogowanegouzytkownika" id="skrypty-scriptservice-pobieraniekontekstuzalogowanegouzytkownika"></a>

{% hint style="warning" %}
Feature available from platform version: 2.3403.0

The feature works on requests run in the webforms application with configured OAuth authorization.\
However, it does not work on requests run in the Eximee Dashboard application
{% endhint %}

It is possible to retrieve the context of the currently logged-in user who is filling out the request.

<details>

<summary>API</summary>

```
/**
 * Form security context, containing security-related data. After implementing this topic, it will contain the identity of the current user.
 */
interface SecurityContext {
  /**
   * Identity of the current user
   */
  getCurrentUser(): Identity | null;
}
 
interface ScriptApi {
  // ...
  /**
   * Retrieve the current user context
   */
  getSecurityContext(): SecurityContext;
}
 
/**
 * Representation of a user role
 */
interface Role {
  name: string;
}
 
/**
 * Representation of a user identity
 */
interface Identity {
  /**
   * User identity identifier. In the context of this topic, always 'current'
   */
  id: string;
  /**
   * User name. In the context of this topic, this is the employee's SKP.
   */
  username?: string;
  /**
   * User roles
   */                              
  roles?: Role[];
 
  /**
   * User first name
   */
  firstName?: string;
 
  /**
   * User last name
   */
  lastName?: string;
}
```

</details>

{% hint style="info" %}
When running the script without a logged-in user context, the method **`context.getSecurityContext()`** will return the value **null**
{% endhint %}

Example use in a user-context script

```javascript
function callService(context) {
    context.getSecurityContext().getCurrentUser();               
  // return: { id: 'current', username: '100001', firstName: 'Jan', lastName: 'Kowalski', roles: [{name: 'role_001'}, {name: 'role_002'}] }
    // Check whether the logged-in user has the "role_001" role.
    var czyMaRole = context.getSecurityContext().getCurrentUser().roles.map(x=> x.name).includes("role_001");
     
    // Retrieve the login of the currently logged-in user. 
    var username = context.getSecurityContext().getCurrentUser().username;
}

```

## Retrieving application configuration <a href="#skrypty-scriptservice-pobieraniekonfiguracjiaplikacji" id="skrypty-scriptservice-pobieraniekonfiguracjiaplikacji"></a>

{% hint style="warning" %}
Feature available from platform version: 3.190.0

The feature works on requests run as [Business application](https://docs.eximee.com/documentation/documentation-en/wprowadzenie/aplikacja-biznesowa)
{% endhint %}

{% hint style="info" %}
The application configuration retrieval feature is available for scripts (scriptService), script tasks (scriptTask), and script validators (validationScript).
{% endhint %}

In scripts, it is possible to retrieve application configuration

```javascript
interface Api {
    config: {
        v1: {
          get(key: string): string;
          getOrDefault(key: string, defaultValue: string): string;
        }
    }
}
```

Example script retrieving configuration:

```javascript

function callService(context) {
    let value1
    try {
        value1 = api.config.v1.get("application.url");
    } catch (e) {
        value1 = "default"
    }
 
    let value2 = api.config.v1.get("application.url");
 
    let value3 = api.config.v1.getOrDefault("not_existing_key", "defaultValue")
 
    return [{'value1': value1, 'value2': value2, 'value3': value3}];
}

```

## Processing xlsx files <a href="#skrypty-scriptservice-przetwarzanieplikowxlsx" id="skrypty-scriptservice-przetwarzanieplikowxlsx"></a>

{% hint style="warning" %}
Required module xlsx-sheet-handling-module
{% endhint %}

It is possible to read xlsx files attached to the request. To do this, use the xlsx-sheet-handling-module and the endpoint it provides */xlsx/v1* - the module is currently provided independently, an appropriate access configuration to the service must be set up in ScriptApi. The following request should be passed to the module:

```javascript
{
    content: string
}
```

where content contains the file in base64 format. In this form it is passed to the service when we map the uploader to input (in the case of multiple files in the uploader, it is a list). In response we will receive a map divided into: sheet → row → column

```javascript
{
    "sheet": {
        "1": {
            "A": { "test"
                }
            }
    }
}
```

Example:

```javascript
function callService(context) {
    let files = context.getParameters("uploader");
 
    if (files.size() === 0) {
        return [{}];
    }
 
    let header = {};
    let body = {'content': files.get(0)}
 
    try {
        let response = api.rest.v1.post('excelDocument', { pathParams: ['xlsx', 'v1'] }, header , body);
     
        return [{'output': JSON.stringify(response.body)}];
    } catch (error) {
        Logger.info(error );
        return [{'output': 'An error occurred'}];
    }
 
}
```

## Retrieving Content content <a href="#skrypty-scriptservice-pobieraniezawartoscitresci" id="skrypty-scriptservice-pobieraniezawartoscitresci"></a>

{% hint style="warning" %}
Functionality available from platform version: **3.332.0**
{% endhint %}

From the script level, it is possible to retrieve the content of an artifact **Content** (textContent) created in Eximee Designer, using the function: `api.repository.v1.textContent`. This function returns an object that contains content for each defined translation. To retrieve the content for a given translation, we use the function `language`. Example usage:

```javascript
const nazwaArtefaktu = "footer";
const wersjaArtefaktu = "*";
const jezyk = "pl";
 
const artefaktTresci = api.repository.v1.textContent(nazwaArtefaktu, wersjaArtefakut);
const stopkaPl = artefaktTresci.language(jezyk).text();
```

{% hint style="danger" %}

1. function **`textContent`** throws an exception if it does not find an artifact with the specified parameters
2. function **`language`** throws an exception if it does not find a translation in the specified language
   {% endhint %}

If we are not sure that the parameters we provided are correct, we can handle exceptions using **try catch**:

```javascript
const nazwaArtefaktu = "footer";
const wersjaArtefaktu = "*";
const jezyk = "pl";
 
let artefaktTresci;
try {
    artefaktTresci = api.repository.v1.textContent(nazwaArtefaktu, wersjaArtefaktu);
} catch (error) {
    return [{'output': "No artifact found with name: " + nazwaArtefaktu + " and version: " + wersjaArtefaktu}]
}
 
let stopkaPl;   
try {
    stopkaPl = artefaktTresci.language(jezyk).text();  
} catch (error) {
    return [{'output': "No translation found in the artifact for language: " + jezyk}];
}
 
return [{'output': stopkaPl}];


```

## Mathematical operations in ScriptCode

More information in [Mathematical operations in ScriptCode](https://docs.eximee.com/documentation/documentation-en/budowanie-aplikacji/logika-biznesowa/scriptcode/skrypty-scriptservice/api-skryptow/operacje-matematyczne-w-scriptcode)

## Operations and access to process data

More information in [operacje-i-dostep-do-danych-procesu](https://docs.eximee.com/documentation/documentation-en/budowanie-aplikacji/logika-biznesowa/scriptcode/skrypty-scriptservice/api-skryptow/operacje-i-dostep-do-danych-procesu "mention")

## Retrieving request statuses

More information in [Retrieving request statuses](https://docs.eximee.com/documentation/documentation-en/budowanie-aplikacji/logika-biznesowa/scriptcode/skrypty-scriptservice/api-skryptow/pobieranie-statusow-wnioskow)

## Data model

More information in [Data model API](https://docs.eximee.com/documentation/documentation-en/budowanie-aplikacji/model-danych/wykorzystanie-modelu-danych/api-modelu-danych)

## Other examples

### Populating the value picker component (Combobox) <a href="#skrypty-scriptservice-przykladowyskryptzasilajacykomponentpolewyboruwartoscizlisty-combobox-example" id="skrypty-scriptservice-przykladowyskryptzasilajacykomponentpolewyboruwartoscizlisty-combobox-example"></a>

The script populates **the value picker** combobox based on the passed 'locale' parameter.

Outputs **id**, **label**, and **description** should be attached to the appropriate combobox fields, respectively **id**, **text**, and **description**.

Combobox-populating script:

```javascript
function callService(context) {
    const locale = context.getFirstParameter('locale');
    if (locale === 'pl') {
        return [
            { 'id': 1, 'label': 'Value 1', 'description': 'Description 1', },
            { 'id': 2, 'label': 'Value 2', 'description': 'Description 2' }
        ];
    } else if (locale === 'en') {
        return [
            { 'id': 1, 'label': 'Label 1', 'description': 'Description 1', },
            { 'id': 2, 'label': 'Label 2', 'description': 'Description 2' }
        ];
    } else {
        return [
            { 'id': 1, 'label': 'Value 1 in the default translation', 'description': 'Description 1', },
            { 'id': 2, 'label': 'Value 2 in the default translation', 'description': 'Description 2' }
        ];
    }
}
```

### Retrieving file names from the Attachments component

The script uses the method **getData()**, which retrieves the value of a specific attribute for the component with the specified id. For the component **Attachments** the names of added files are specified using the attribute **fileNames** and come from the request as a string in JSON format (e.g. *\["name1.png", "name2.png"]*), therefore it is necessary to convert this string into an object using the method **JSON.parse**.

{% hint style="warning" %}
**NOTE!** Method **getData()** uses the component id, so the script is not universal - for each request the appropriate component id must be provided **Attachments**:
{% endhint %}

```javascript
function callService(context) {
    const zalacznik1 = JSON.parse(context.getData("@GesUploadFile1", "fileNames"));
    const zalacznik2 = JSON.parse(context.getData("@GesUploadFile2", "fileNames"));
}
```

### Retrieving the value of the value picker component (Combobox) from a repeatable section <a href="#skrypty-scriptservice-fragmentskryptupobierajacegowartoscipolawyboruwartoscizlisty-comboboxa-zsekcji" id="skrypty-scriptservice-fragmentskryptupobierajacegowartoscipolawyboruwartoscizlisty-comboboxa-zsekcji"></a>

The script uses the method **getData()**, which retrieves the value of a specific attribute for the component with the specified id. For the component **Value selection field from a list** the values (labels) of the given keys are specified using the attribute **label.**

```javascript
for (let i=0; i<10; i++)  {
        let id = "GesRepeatableSection1.row" + i + ".GesCombobox1";
        let etykieta = context.getData(id, "label");
}
```
