8.1:documentation:transformation_scripts:dev:scripts

Groovy skripty a jejich oprávnění

TODO: translate to EN

Skripty nám umožňují měnit chování aplikace bez nutnosti jejího restartu. Dalším důvodem bylo zpřístupnění pouze některých tříd a servis ve skriptech, více viz Oprávnění skriptů. Skripty se navíc mohou mezi sebou volat navzájem, zavolat skript můžeme pomocí evaluátoru viz sekce scriptEvaluator.

Pro zjednodušení práce s nimi existuje na frontendu agenda která umožňuje základní CRUD operace (vytvoření, čtení, update a smazání). Základní definice skriptu při vytváření vyžaduje vyplnění unikátního kódu, neunikátní jméno a kategorii, jako dobrovolný atribut je zde popis skriptu, do kterého je doporučeno uvést jaké parametry tento skript přijímá a jaký datový typ vrací. Po vytvoření skriptu je dovoleno přidat ke skriptu jednotlivá oprávnění. Více viz sekce Oprávnění skriptů. Tělo skriptu je v jazyce groovy více o jazyku a jeho syntaxi.

V průběhu evaluování skriptu jsou dynamicky kontrolovány jednotlivé třídy v tělě skriptu a je ověřována jejich existence v povolených třídách. O tuto kontrolu se stará GroovySandboxFilter, přesněji metoda filter. V této metodě dochází k převodu tříd obalených proxy na prosté třídy. Tento systém kontroly zabraňuje nechtěnému použití jiných než povolených tříd.

Po vytvoření skriptu mu můžeme přiřadit oprávnění. Jedná se o třídy a služby, které může daný skript používat. Dané oprávnění se vztahuje pouze pro daný skript, ke kterému patří. Skripty mezi sebou oprávnění nedědí, ani nijak nepřebírají.

Oprávnění uvedené v tabulce se řadí do dvou základních typů.

Standardně povolené třídy

Zde je seznam standardně povolených tříd, tyto třídy jsou vyjmenované v GroovySandboxFilter:

String.class, Integer.class, Double.class, Long.class, Date.class, Enum.class, Boolean.class, 
BigDecimal.class, UUID.class, Character.class, GuardedString.class, DateTimeFormatter.class,
DateTimeFormat.class, DateTime.class, String[].class, LocalDateTime.class,
Map.class, HashMap.class, List.class, ArrayList.class, Set.class, HashSet.class,
LoggerFactory.class, Logger.class, ch.qos.logback.classic.Logger.class

Aby mohl skript volat jiné skripty je potřeba mu povolit použití scriptEvaluator (Pokud je tato service dostupná v předkovi skriptu, může být do potomka poslána skrze Builder, je žádoucí aby byla poslána pod názvem proměnné scriptEvaluator).

Příklad zavolání jiného skriptu s kódem 'test':

def result = scriptEvaluator.evaluate(
    scriptEvaluator.newBuilder()
        .setScriptCode('test')
        .addParameter('scriptEvaluator', scriptEvaluator)
        .addParameter('text', 'toto je ukázkový text')
	.build());

Zavolali jsme skript test, kterému jsme předali scriptEvaluator, takže může volat další skripty (Pozor pouze ze stejné kategorie), dále jsme skriptu předali proměnou text ve které se nachází následující text: toto je ukázkový text. Návratová hodnota skriptu bude v proměnné result. Je důležité, aby tento skript měl práva na třídu, kterou skript test vrací.

Pozor! Oprávnění použité ve skriptu se týká pouze tohoto daného skriptu, pokud je skript volán z jiného skriptu. Nepřebírá původní skript oprávnění podřazeného ani nadřazeného (skript obsahuje pouze svoje oprávnění).

Momentálně není ošetřeno zacyklení skriptů. tj. budeme-li volat rodiče z potomka dojde k zacyklení. Tento stav je potřeba ošetřit při psaní skriptů.

// příklad zacyklení skriptů, mějme dva skript A (code: parent) a skript B (code: child)
// Skript A (code: parent)
scriptEvaluator.evaluate(
    scriptEvaluator.newBuilder()
        .setScriptCode('child')
	.build());

// Skript B (code: child)
scriptEvaluator.evaluate(
    scriptEvaluator.newBuilder()
        .setScriptCode('parent')
	.build());
// skripty se navzájem provolávají do nekonečna.
Pozor pokud není na konci skriptu uveden return, skript vrátí poslední atribut

Pokud ve skriptu použijeme jiné než povolené třídy bude vyhozena chyba IdmSecurityException, která je odchycena v AbstractScriptEvaluator, kde je doplněna o kód, název a kategorii skriptu. Toto log je momentálně pouze pro skripty volané přes scriptEvalutor.

Skript ve skriptu můžeme pustit pomocí instance scriptEvaluator jedná se o potomka třídy AbstractScriptEvaluator. Potomci jsou rozděleni podle ScriptCategoryEnum. Je tedy možné volat pouze scripty ze stejné kategorie (Standardní kategorie může být volána ze všech), jako je původní scriptEvaluator. Momentálně pouze standardní kategorie může být volána z jiné.

Jako parametr metody evaluate v scriptEvaluator je předán Builder, který může být do budoucna obohacen o další atributy. Momentálně je mu nutné pouze předat kód scriptu. Jako další je možné přidat parametry, příklad použití:

scriptEvaluator.evaluate(
    scriptEvaluator.newBuilder()
        .setScriptCode('SCRIPT_CODE_001')
        .addParameter('scriptEvaluator', scriptEvaluator)
        .addParameter('attributeValue', attributeValue)
        .addParameter('entity', entity)
        .addParameter('text', 'test')
	.build());

DefaultTransformToResourceEvaluator

DefaultTransformToResourceEvaluator - evaluátor používaný pro mapování do systému, lze z něj spouštět pouze scripty v kategorii TRANSFORM_TO. Automaticky je přidán do prvního scriptu v transformaci do systému na detailu mapování.

DefaultTransformFromResourceEvaluator

DefaultTransformFromResourceEvaluator - evaluátor používaný pro mapování ze systému, lze z něj spouštět pouze scripty v kategorii TRANSFORM_FROM. Automaticky je přidán do prvního scriptu v transformaci do systému na detailu mapování.

DefaultScriptEvaluator

DefaultScriptEvaluator - evaluátor používaný defaultní skripty. Defaultní skripty, jako jedné mohou být volané mimo svou kategorii.

Pro zjednodušení byl na detailu mapování vytvořen select box, který dovulje vybrat skript z jednotlivých kategorií a zároveň předvyplňuje volání skriptů. Příklad použití skriptů je obdržen z netrimmed IdmScriptDTO. Součást šablony pro volání je i kód a popis skriptu.

Do každého skriptu je explicitně povoleno používat logger viz povolené třídy. Příklad zavolání logeru a zalogování hlášky:

org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger("script_name");
LOG.info("Toto je výpis do INFO logu");
LOG.error("Toto je výpis do ERROR logu");
LOG.warn("Toto je výpis do WARN logu");

Pokud chcete debug hlášky je potřeba upravit logback-spring.xml, případně doplnit package do názvu logeru viz:

org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger("eu.bcvsolutions.idm.script_name");
LOG.debug("Toto je výpis do DEBUG logu");

Pro zjednodušení vývoje není doporučeno používat klasický System.out.println, ale přímo funkci groovy println, tato funkce nevyžaduje žádná další oprávnění, příklad použití:

println("test")

S groovy skripty se můžeme setkat při transformací do a ze systému, kde mimo jiné můžeme naspat vlastní skript. Tento skript má pouze standardní oprávnění a není možné k němu přidat další. Pomocí scriptEvaluator můžeme ovšem zavolat jiný skript. Skript do transformace vložíme pomocí tlačítka Vložit skript.