# API skryptów

## Pobieranie danych wejściowych <a href="#skrypty-scriptservice-pobieraniedanychwejsciowych" id="skrypty-scriptservice-pobieraniedanychwejsciowych"></a>

Poniżej znajduje się przykładowy kod, który pokazuje, w jaki sposób można korzystać z wejścia oraz jak należy zwrócić dane.

```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}];
}
```

## Wejście do Skrypt serwisu <a href="#skrypty-scriptservice-wejsciedoskryptserwisu" id="skrypty-scriptservice-wejsciedoskryptserwisu"></a>

Jako wejście (parametr *context* w powyższym kodzie) znajduje się obiekt, który udostępnia następujące metody:

* List\<String> **getParameters**(final String name); - pobiera konkretną listę z mapy danych wejściowych,
* String **getFirstParameter**(final String name); - pobiera pierwszy element z konkretnej listy z mapy danych wejściowych lub zwraca wartość "null",
* String **getFirstParameter**(final String name, final String defaultIfEmpty); - pobiera pierwszy element z konkretnej listy z mapy danych wejściowych, a jeśli go nie ma lub jest pusty to zwraca wartość "defaultIfEmpty",
* Map\<String, List\<String>> **getInputParameters**(); - zwraca całą mapę danych wejściowych, którą przechowuje,
* String **isVisible**(final String id); - pobiera informację, czy komponent o wskazanym id jest widoczny,
* String **getValue**(final String id); - pobiera wartość komponentu (również zmiennej sesyjnej) o wskazanym id,
* void **setValue**(final String componentId, final String value); - ustawia wartość **value** komponentu o wskazanym **componentId**. Zasada podawania componentId jest taka sama jak przy getValue.\
  Wywołując skrypt wewnątrz komponentu złożonego, w celu ustawienia wartości komponentu / zmiennej sesyjnej osadzonej w tym samym komponencie, należy componentId prefiksować znakiem **@**

> **UWAGA!** Próba wykonania **setValue** na komponencie, który nasłuchuje na inny komponent lub na komponencie, **na** którego nasłuchują inne komponenty, poskutkuje rzuceniem wyjątku.

* String **getData**(final String id, final String attribute); - pobiera wartość konkretnego atrybutu dla komponentu o wskazanym id

> **UWAGA!** Możliwe do pobrania atrybuty są zależne od komponentu.

* List\<Map\<String, String>> **callService**(String name, Map\<String, List\<String>> input); - umożliwia wołanie ServiceProxy,
* ValidatorOutput **validate**(String name, Map\<String, List\<String>> input); - umożliwia wołanie walidatora

## Wyjście ze Skrypt serwisu <a href="#skrypty-scriptservice-wyjsciezeskryptserwisu" id="skrypty-scriptservice-wyjsciezeskryptserwisu"></a>

Wynik wykonania skryptu możemy zwrócić na dwa sposoby:

1. **Lista map** - tablica obiektów na wyjściu jest parsowana na listę map, po czym zostaje przetwarzana w standardowy sposób.
2. **Pojedyncza mapa** - zwrotka zostaje opakowana jako lista jednoelementowa, po czym przetwarzana jest w standardowy sposób.

\
Wyjście z skryptu z przykładową listą:

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

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

Logowanie danych wejściowych jest automatyczne

Logowanie ręczne powinno być zgodne z opisem w [Logowanie w ScriptCode](https://docs.eximee.com/budowanie-aplikacji/logika-biznesowa/scriptcode/logowanie-w-scriptcode)

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

## Rzucanie wyjątków biznesowych

Błędy biznesowe zostały szczegółowo opisane na stronie [Strony błędów](https://docs.eximee.com/interfejs-uzytkownika/formularze/tworzenie-formularza/strony-bledow#bledy-biznesowe).

## Zapis danych tymczasowych <a href="#skrypty-scriptservice-zapisdanychtymczasowych" id="skrypty-scriptservice-zapisdanychtymczasowych"></a>

Możliwe jest zapisanie pewnych danych pomiędzy wywołaniami serwisu. Służy do tego obiekt `registry`.

Możliwe metody, które można wywołać na obiekcie `registry`:

* save(namespace, id, data) - zapis danych
* saveVersion(namespace, id, data, version) - zapis danych z weryfikacją wersji. Jeśli pole version jest nieustawione lub jest puste - zapis bez weryfikacji. Jeśli wartość pola version nie zgadza się z aktualną wersją przechowywanych danych, wniosek zakończy się z błędem.
* get(namespace, id) - pobranie danych
* getVersion(namespace, id) - pobranie obiektu { data: 'zapisane dane', version: 'aktualna wersja danych' }
* delete(namespace, id) - usuwanie danych.

Dane przechowywane są w przestrzeniach nazw (namespace w metodach) ustalanych przez administratorów. Każdej przestrzeni można ustawić czas życia danych. Domyślnie jest to **90 dni**. Podanie tego samego id w ramach kilku wywołań zapisu powoduje aktualizację danych powiązanych z danym id. Wersja danych to unikalny identyfikator generowany przy każdym zapisie danych.

{% hint style="info" %}
Obiekt to poprawnego działania wymaga działającej aplikacji EximeeRegistry.
{% endhint %}

{% hint style="danger" %}
**Zapis danych za pomocą EximeeRegistry nie służy do zapisu czegokolwiek na stałe!**
{% endhint %}

**Przykłady:**

```javascript
context.registry().save('test', '123123', 'dane tymczasowe');
context.registry().get('test', '123123'); // zwróci 'dane tymczasowe'
const item = context.registry().getVersion('test', '123123'); // zwróci obiekt {data,version}
context.registry().saveVersion('test','123123', 'nowe dane', item.version); // wersja zgodna zapis się uda
context.registry().saveVersion('test','123123', 'nowe dane', 'jakaś inna wersja'); // wersja niezgodna zapis się nie uda
context.registry().delete('test','123123');

```

## Pobieranie kontekstu zalogowanego użytkownika <a href="#skrypty-scriptservice-pobieraniekontekstuzalogowanegouzytkownika" id="skrypty-scriptservice-pobieraniekontekstuzalogowanegouzytkownika"></a>

{% hint style="warning" %}
Funkcjonalność dostępna od wersji platformy: 2.3403.0

Funkcjonalność działa na wnioskach uruchamianych w aplikacji webforms ze skonfigurowaną autoryzacją oauth.\
Nie działa natomiast na wnioskach uruchamianych w aplikacji Eximee Dashboard
{% endhint %}

Istnieje możliwość pobrania kontekstu aktualnie zalogowanego użytkownika, który uzupełnia wniosek.

<details>

<summary>API</summary>

```
/**
 * Kontekst security formularza, zawierające dane dotyczące bezpieczeństwa. Po realizacji tego tematu będzie zawierać tożsamość aktualnego użytkownika.
 */
interface SecurityContext {
  /**
   * Tożsamość aktualnego użytkownika
   */
  getCurrentUser(): Identity | null;
}
 
interface ScriptApi {
  // ...
  /**
   * Pobranie aktualnego kontekstu użytkownika
   */
  getSecurityContext(): SecurityContext;
}
 
/**
 * Reprezentacja roli użytkownika
 */
interface Role {
  name: string;
}
 
/**
 * Reprezentacja tożsamości użytkownika
 */
interface Identity {
  /**
   * Identyfikator tożsamości użytkownika. W kontekście tego tematu zawsze 'current'
   */
  id: string;
  /**
   * Nazwa użytkownika. W kontekście tego tematu jest to SKP pracownika.
   */
  username?: string;
  /**
   * Role użytkownika
   */                              
  roles?: Role[];
 
  /**
   * Imię użytkownika
   */
  firstName?: string;
 
  /**
   * Nazwisko użytkownika
   */
  lastName?: string;
}
```

</details>

{% hint style="info" %}
Podczas uruchomienia skryptu bez zalogowanego kontekstu użytkownika metoda **`context.getSecurityContext()`** zwróci wartość **null**
{% endhint %}

Przykładowe użycie w skrypcie kontekstu użytkownika

```javascript
function callService(context) {
    context.getSecurityContext().getCurrentUser();               
  // return: { id: 'current', username: '100001', firstName: 'Jan', lastName: 'Kowalski', roles: [{name: 'role_001'}, {name: 'role_002'}] }
    // Sprawdzanie czy zalogowany użytkownik ma role "role_001".
    var czyMaRole = context.getSecurityContext().getCurrentUser().roles.map(x=> x.name).includes("role_001");
     
    // Pobranie loginu aktualnie zalogowanego użytkownika. 
    var username = context.getSecurityContext().getCurrentUser().username;
}

```

## Pobieranie konfiguracji aplikacji <a href="#skrypty-scriptservice-pobieraniekonfiguracjiaplikacji" id="skrypty-scriptservice-pobieraniekonfiguracjiaplikacji"></a>

{% hint style="warning" %}
Funkcjonalność dostępna od wersji platformy: 3.190.0

Funkcjonalność działa na wnioskach uruchamianych jako [Aplikacja biznesowa](https://docs.eximee.com/wprowadzenie/aplikacja-biznesowa)
{% endhint %}

{% hint style="info" %}
Funkcjonalność pobierania konfiguracji aplikacji jest dostępna dla skryptów (scriptService), zadań skryptowych (scriptTask) oraz walidatorów skryptowych (validationScript).
{% endhint %}

W skryptach istnieje możliwość pobrania konfiguracji aplikacji

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

Przykładowy skrypt pobierający konfiguracje:

```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}];
}

```

## Przetwarzanie plików xlsx <a href="#skrypty-scriptservice-przetwarzanieplikowxlsx" id="skrypty-scriptservice-przetwarzanieplikowxlsx"></a>

{% hint style="warning" %}
Potrzebny moduł xlsx-sheet-handling-module
{% endhint %}

Możliwy jest odczyt plików xlsx załączonych na wniosku. W tym celu należy skorzystać z modułu xlsx-sheet-handling-module oraz dostarczanego przez niego endpointu */xlsx/v1* - moduł aktualnie dostarczany jest niezależnie, należy wykonać odpowiednią konfiguracje dostępu do usługi w ScriptApi. Do modułu należy przekazać request:

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

gdzie w content przekazujemy plik w formie base64. W takiej formie jest przekazywany do serwisu gdy zmapujemy na wejście uploader (w przypadku wielu plików w uploaderze jest to lista). W odpowiedzi otrzymamy mapę z podziałem: arkusz → wiersz → kolumna

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

Przykład:

```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': 'Wystapil blad'}];
    }
 
}
```

## Pobieranie zawartości Treści <a href="#skrypty-scriptservice-pobieraniezawartoscitresci" id="skrypty-scriptservice-pobieraniezawartoscitresci"></a>

{% hint style="warning" %}
Funkcjonalność dostępna od wersji platformy: **3.332.0**
{% endhint %}

Z poziomu skryptu można pobrać zawartość artefaktu **Treść** (textContent) utworzoną w Eximee Designer, wykorzystując do tego funkcję: `api.repository.v1.textContent`. Funkcja ta zwraca obiekt, który posiada treści dla każdego ze zdefiniowanych tłumaczeń. By pobrać treść dla danego tłumaczenia używamy funkcji `language`. Przykład użycia:

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

{% hint style="danger" %}

1. funkcja **`textContent`** rzuca wyjątek jeśli nie znajdzie artefaktu o podanych parametrach
2. funkcja **`language`** rzuca wyjątek jeśli nie znajdzie tłumaczenia w podanym języku
   {% endhint %}

Jeśli nie mamy pewności, że podane przez nas parametry są prawidłowe, możemy obsłużyć wyjątki używając **try catch**:

```javascript
const nazwaArtefaktu = "stopka";
const wersjaArtefaktu = "*";
const jezyk = "pl";
 
let artefaktTresci;
try {
    artefaktTresci = api.repository.v1.textContent(nazwaArtefaktu, wersjaArtefaktu);
} catch (error) {
    return [{'output': "Brak artefaktu o nazwie: " + nazwaArtefaktu + " i wersji: " + wersjaArtefaktu}]
}
 
let stopkaPl;   
try {
    stopkaPl = artefaktTresci.language(jezyk).text();  
} catch (error) {
    return [{'output': "W artefakcie nie znaleziono tłumaczenia dla języka: " + jezyk}];
}
 
return [{'output': stopkaPl}];


```

## Operacje matematyczne w ScriptCode

Więcej informacji w [Operacje matematyczne w ScriptCode](https://docs.eximee.com/budowanie-aplikacji/logika-biznesowa/scriptcode/skrypty-scriptservice/api-skryptow/operacje-matematyczne-w-scriptcode)

## Operacje i dostęp do danych procesu

Więcej informacji w [operacje-i-dostep-do-danych-procesu](https://docs.eximee.com/budowanie-aplikacji/logika-biznesowa/scriptcode/skrypty-scriptservice/api-skryptow/operacje-i-dostep-do-danych-procesu "mention")

## Pobieranie statusów wniosków

Więcej informacji w [Pobieranie statusów wniosków](https://docs.eximee.com/budowanie-aplikacji/logika-biznesowa/scriptcode/skrypty-scriptservice/api-skryptow/pobieranie-statusow-wnioskow)

## Model danych

Więcej informacji w [API modelu danych](https://docs.eximee.com/budowanie-aplikacji/model-danych/wykorzystanie-modelu-danych/api-modelu-danych)

## Inne przykłady

### Zasilenie komponentu Pole wyboru wartości z listy (Combobox) <a href="#skrypty-scriptservice-przykladowyskryptzasilajacykomponentpolewyboruwartoscizlisty-combobox-example" id="skrypty-scriptservice-przykladowyskryptzasilajacykomponentpolewyboruwartoscizlisty-combobox-example"></a>

Skrypt zasila **Pole wyboru wartości** z listy (Combobox) na podstawie przekazanego parametru 'locale'.

Wyjścia **id**, **label**, oraz **description** należy podpiąć pod odpowiednie pola comboboxa, kolejno **id**, **text**, oraz **description**.

Skrypt zasilający combobox:

```javascript
function callService(context) {
    const locale = context.getFirstParameter('locale');
    if (locale === 'pl') {
        return [
            { 'id': 1, 'label': 'Wartosc 1', 'description': 'Opis 1', },
            { 'id': 2, 'label': 'Wartosc 2', 'description': 'Opis 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': 'Wartosc domslnego tlumaczenia 1', 'description': 'Opis 1', },
            { 'id': 2, 'label': 'Wartosc domslnego tlumaczenia 2', 'description': 'Opis 2' }
        ];
    }
}
```

### Pobieranie nazwy plików z komponentu Załączniki

Skrypt wykorzystuje metodę **getData()**, która pobiera wartość konkretnego atrybutu dla komponentu o wskazanym id. Dla komponentu **Załączniki** nazwy dodanych plików określane są za pomocą atrybutu **fileNames** i przychodzą z wniosku jako string w formacie JSON (np. *\["name1.png", "name2.png"]*), dlatego konieczne jest przekształcenie tego stringa w obiekt za pomocą metody **JSON.parse**.

{% hint style="warning" %}
**UWAGA!** Metoda **getData()** wykorzystuje id komponentu, więc skrypt nie jest uniwersalny - dla każdego wniosku należy podać odpowiednie id komponentu **Załączniki**:
{% endhint %}

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

### Pobieranie wartości Pola wyboru wartości z listy (Comboboxa) z sekcji powtarzalnej <a href="#skrypty-scriptservice-fragmentskryptupobierajacegowartoscipolawyboruwartoscizlisty-comboboxa-zsekcji" id="skrypty-scriptservice-fragmentskryptupobierajacegowartoscipolawyboruwartoscizlisty-comboboxa-zsekcji"></a>

Skrypt wykorzystuje metodę **getData()**, która pobiera wartość konkretnego atrybutu, dla komponentu o wskazanym id. Dla komponentu **Pole wyboru wartości z listy** wartości (etykiety) danych kluczy określane są za pomocą atrybutu **label.**

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