mirror of
https://github.com/danieldemus/openhab-core.git
synced 2025-01-10 21:31:53 +01:00
Allow DSL scripts for script transformation (#2990)
* Allow DSL scripts for script transformation Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
parent
663240b040
commit
293de9d6db
@ -51,6 +51,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
public class ScriptTransformationService
|
public class ScriptTransformationService
|
||||||
implements TransformationService, RegistryChangeListener<TransformationConfiguration> {
|
implements TransformationService, RegistryChangeListener<TransformationConfiguration> {
|
||||||
public static final String OPENHAB_TRANSFORMATION_SCRIPT = "openhab-transformation-script-";
|
public static final String OPENHAB_TRANSFORMATION_SCRIPT = "openhab-transformation-script-";
|
||||||
|
public static final String SUPPORTED_CONFIGURATION_TYPE = "script";
|
||||||
|
|
||||||
private static final Pattern SCRIPT_CONFIG_PATTERN = Pattern
|
private static final Pattern SCRIPT_CONFIG_PATTERN = Pattern
|
||||||
.compile("(?<scriptType>.*?):(?<scriptUid>.*?)(\\?(?<params>.*?))?");
|
.compile("(?<scriptType>.*?):(?<scriptUid>.*?)(\\?(?<params>.*?))?");
|
||||||
@ -100,12 +101,15 @@ public class ScriptTransformationService
|
|||||||
TransformationConfiguration transformationConfiguration = transformationConfigurationRegistry
|
TransformationConfiguration transformationConfiguration = transformationConfigurationRegistry
|
||||||
.get(scriptUid);
|
.get(scriptUid);
|
||||||
if (transformationConfiguration != null) {
|
if (transformationConfiguration != null) {
|
||||||
|
if (!SUPPORTED_CONFIGURATION_TYPE.equals(transformationConfiguration.getType())) {
|
||||||
|
throw new TransformationException("Configuration does not have correct type 'script' but '"
|
||||||
|
+ transformationConfiguration.getType() + "'.");
|
||||||
|
}
|
||||||
script = transformationConfiguration.getContent();
|
script = transformationConfiguration.getContent();
|
||||||
}
|
}
|
||||||
if (script == null) {
|
if (script == null) {
|
||||||
throw new TransformationException("Could not get script for UID '" + scriptUid + "'.");
|
throw new TransformationException("Could not get script for UID '" + scriptUid + "'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptCache.put(scriptUid, script);
|
scriptCache.put(scriptUid, script);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +141,7 @@ public class ScriptTransformationService
|
|||||||
ScriptEngine engine = compiledScript != null ? compiledScript.getEngine()
|
ScriptEngine engine = compiledScript != null ? compiledScript.getEngine()
|
||||||
: scriptEngineContainer.getScriptEngine();
|
: scriptEngineContainer.getScriptEngine();
|
||||||
ScriptContext executionContext = engine.getContext();
|
ScriptContext executionContext = engine.getContext();
|
||||||
executionContext.setAttribute("inputString", source, ScriptContext.ENGINE_SCOPE);
|
executionContext.setAttribute("input", source, ScriptContext.ENGINE_SCOPE);
|
||||||
|
|
||||||
String params = configMatcher.group("params");
|
String params = configMatcher.group("params");
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
|
@ -46,13 +46,18 @@ import org.openhab.core.transform.TransformationException;
|
|||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||||
public class ScriptTransformationServiceTest {
|
public class ScriptTransformationServiceTest {
|
||||||
private static final String SCRIPT_TYPE = "script";
|
private static final String SCRIPT_LANGUAGE = "customDsl";
|
||||||
private static final String SCRIPT_UID = "scriptUid";
|
private static final String SCRIPT_UID = "scriptUid";
|
||||||
|
private static final String INVALID_SCRIPT_UID = "invalidScriptUid";
|
||||||
|
|
||||||
private static final String SCRIPT = "script";
|
private static final String SCRIPT = "script";
|
||||||
private static final String SCRIPT_OUTPUT = "output";
|
private static final String SCRIPT_OUTPUT = "output";
|
||||||
|
|
||||||
private static final TransformationConfiguration TRANSFORMATION_CONFIGURATION = new TransformationConfiguration(
|
private static final TransformationConfiguration TRANSFORMATION_CONFIGURATION = new TransformationConfiguration(
|
||||||
SCRIPT_UID, "label", "script", null, SCRIPT);
|
SCRIPT_UID, "label", ScriptTransformationService.SUPPORTED_CONFIGURATION_TYPE, null, SCRIPT);
|
||||||
|
private static final TransformationConfiguration INVALID_TRANSFORMATION_CONFIGURATION = new TransformationConfiguration(
|
||||||
|
INVALID_SCRIPT_UID, "label", "invalid", null, SCRIPT);
|
||||||
|
|
||||||
private @Mock @NonNullByDefault({}) TransformationConfigurationRegistry transformationConfigurationRegistry;
|
private @Mock @NonNullByDefault({}) TransformationConfigurationRegistry transformationConfigurationRegistry;
|
||||||
private @Mock @NonNullByDefault({}) ScriptEngineManager scriptEngineManager;
|
private @Mock @NonNullByDefault({}) ScriptEngineManager scriptEngineManager;
|
||||||
private @Mock @NonNullByDefault({}) ScriptEngineContainer scriptEngineContainer;
|
private @Mock @NonNullByDefault({}) ScriptEngineContainer scriptEngineContainer;
|
||||||
@ -65,29 +70,37 @@ public class ScriptTransformationServiceTest {
|
|||||||
public void setUp() throws ScriptException {
|
public void setUp() throws ScriptException {
|
||||||
service = new ScriptTransformationService(transformationConfigurationRegistry, scriptEngineManager);
|
service = new ScriptTransformationService(transformationConfigurationRegistry, scriptEngineManager);
|
||||||
|
|
||||||
when(scriptEngineManager.createScriptEngine(eq(SCRIPT_TYPE), any())).thenReturn(scriptEngineContainer);
|
when(scriptEngineManager.createScriptEngine(eq(SCRIPT_LANGUAGE), any())).thenReturn(scriptEngineContainer);
|
||||||
when(scriptEngineManager.isSupported(anyString()))
|
when(scriptEngineManager.isSupported(anyString()))
|
||||||
.thenAnswer(scriptType -> SCRIPT_TYPE.equals(scriptType.getArgument(0)));
|
.thenAnswer(arguments -> SCRIPT_LANGUAGE.equals(arguments.getArgument(0)));
|
||||||
when(scriptEngineContainer.getScriptEngine()).thenReturn(scriptEngine);
|
when(scriptEngineContainer.getScriptEngine()).thenReturn(scriptEngine);
|
||||||
when(scriptEngine.eval(SCRIPT)).thenReturn("output");
|
when(scriptEngine.eval(SCRIPT)).thenReturn("output");
|
||||||
when(scriptEngine.getContext()).thenReturn(scriptContext);
|
when(scriptEngine.getContext()).thenReturn(scriptContext);
|
||||||
|
|
||||||
when(transformationConfigurationRegistry.get(anyString())).thenAnswer(
|
when(transformationConfigurationRegistry.get(anyString())).thenAnswer(arguments -> {
|
||||||
scriptUid -> SCRIPT_UID.equals(scriptUid.getArgument(0)) ? TRANSFORMATION_CONFIGURATION : null);
|
String scriptUid = arguments.getArgument(0);
|
||||||
|
if (SCRIPT_UID.equals(scriptUid)) {
|
||||||
|
return TRANSFORMATION_CONFIGURATION;
|
||||||
|
} else if (INVALID_SCRIPT_UID.equals(scriptUid)) {
|
||||||
|
return INVALID_TRANSFORMATION_CONFIGURATION;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void success() throws TransformationException {
|
public void success() throws TransformationException {
|
||||||
String returnValue = Objects.requireNonNull(service.transform(SCRIPT_TYPE + ":" + SCRIPT_UID, "input"));
|
String returnValue = Objects.requireNonNull(service.transform(SCRIPT_LANGUAGE + ":" + SCRIPT_UID, "input"));
|
||||||
|
|
||||||
assertThat(returnValue, is(SCRIPT_OUTPUT));
|
assertThat(returnValue, is(SCRIPT_OUTPUT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void scriptExecutionParametersAreInjectedIntoEngineContext() throws TransformationException {
|
public void scriptExecutionParametersAreInjectedIntoEngineContext() throws TransformationException {
|
||||||
service.transform(SCRIPT_TYPE + ":" + SCRIPT_UID + "?param1=value1¶m2=value2", "input");
|
service.transform(SCRIPT_LANGUAGE + ":" + SCRIPT_UID + "?param1=value1¶m2=value2", "input");
|
||||||
|
|
||||||
verify(scriptContext).setAttribute(eq("inputString"), eq("input"), eq(ScriptContext.ENGINE_SCOPE));
|
verify(scriptContext).setAttribute(eq("input"), eq("input"), eq(ScriptContext.ENGINE_SCOPE));
|
||||||
verify(scriptContext).setAttribute(eq("param1"), eq("value1"), eq(ScriptContext.ENGINE_SCOPE));
|
verify(scriptContext).setAttribute(eq("param1"), eq("value1"), eq(ScriptContext.ENGINE_SCOPE));
|
||||||
verify(scriptContext).setAttribute(eq("param2"), eq("value2"), eq(ScriptContext.ENGINE_SCOPE));
|
verify(scriptContext).setAttribute(eq("param2"), eq("value2"), eq(ScriptContext.ENGINE_SCOPE));
|
||||||
verifyNoMoreInteractions(scriptContext);
|
verifyNoMoreInteractions(scriptContext);
|
||||||
@ -95,26 +108,26 @@ public class ScriptTransformationServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void invalidScriptExecutionParametersAreDiscarded() throws TransformationException {
|
public void invalidScriptExecutionParametersAreDiscarded() throws TransformationException {
|
||||||
service.transform(SCRIPT_TYPE + ":" + SCRIPT_UID + "?param1=value1&invalid", "input");
|
service.transform(SCRIPT_LANGUAGE + ":" + SCRIPT_UID + "?param1=value1&invalid", "input");
|
||||||
|
|
||||||
verify(scriptContext).setAttribute(eq("inputString"), eq("input"), eq(ScriptContext.ENGINE_SCOPE));
|
verify(scriptContext).setAttribute(eq("input"), eq("input"), eq(ScriptContext.ENGINE_SCOPE));
|
||||||
verify(scriptContext).setAttribute(eq("param1"), eq("value1"), eq(ScriptContext.ENGINE_SCOPE));
|
verify(scriptContext).setAttribute(eq("param1"), eq("value1"), eq(ScriptContext.ENGINE_SCOPE));
|
||||||
verifyNoMoreInteractions(scriptContext);
|
verifyNoMoreInteractions(scriptContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void scriptsAreCached() throws TransformationException {
|
public void scriptsAreCached() throws TransformationException {
|
||||||
service.transform(SCRIPT_TYPE + ":" + SCRIPT_UID, "input");
|
service.transform(SCRIPT_LANGUAGE + ":" + SCRIPT_UID, "input");
|
||||||
service.transform(SCRIPT_TYPE + ":" + SCRIPT_UID, "input");
|
service.transform(SCRIPT_LANGUAGE + ":" + SCRIPT_UID, "input");
|
||||||
|
|
||||||
verify(transformationConfigurationRegistry).get(SCRIPT_UID);
|
verify(transformationConfigurationRegistry).get(SCRIPT_UID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void scriptCacheInvalidatedAfterChange() throws TransformationException {
|
public void scriptCacheInvalidatedAfterChange() throws TransformationException {
|
||||||
service.transform(SCRIPT_TYPE + ":" + SCRIPT_UID, "input");
|
service.transform(SCRIPT_LANGUAGE + ":" + SCRIPT_UID, "input");
|
||||||
service.updated(TRANSFORMATION_CONFIGURATION, TRANSFORMATION_CONFIGURATION);
|
service.updated(TRANSFORMATION_CONFIGURATION, TRANSFORMATION_CONFIGURATION);
|
||||||
service.transform(SCRIPT_TYPE + ":" + SCRIPT_UID, "input");
|
service.transform(SCRIPT_LANGUAGE + ":" + SCRIPT_UID, "input");
|
||||||
|
|
||||||
verify(transformationConfigurationRegistry, times(2)).get(SCRIPT_UID);
|
verify(transformationConfigurationRegistry, times(2)).get(SCRIPT_UID);
|
||||||
}
|
}
|
||||||
@ -138,7 +151,7 @@ public class ScriptTransformationServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void unknownScriptUidThrowsException() {
|
public void unknownScriptUidThrowsException() {
|
||||||
TransformationException e = assertThrows(TransformationException.class,
|
TransformationException e = assertThrows(TransformationException.class,
|
||||||
() -> service.transform(SCRIPT_TYPE + ":" + "foo", "input"));
|
() -> service.transform(SCRIPT_LANGUAGE + ":" + "foo", "input"));
|
||||||
|
|
||||||
assertThat(e.getMessage(), is("Could not get script for UID 'foo'."));
|
assertThat(e.getMessage(), is("Could not get script for UID 'foo'."));
|
||||||
}
|
}
|
||||||
@ -148,10 +161,18 @@ public class ScriptTransformationServiceTest {
|
|||||||
when(scriptEngine.eval(SCRIPT)).thenThrow(new ScriptException("exception"));
|
when(scriptEngine.eval(SCRIPT)).thenThrow(new ScriptException("exception"));
|
||||||
|
|
||||||
TransformationException e = assertThrows(TransformationException.class,
|
TransformationException e = assertThrows(TransformationException.class,
|
||||||
() -> service.transform(SCRIPT_TYPE + ":" + SCRIPT_UID, "input"));
|
() -> service.transform(SCRIPT_LANGUAGE + ":" + SCRIPT_UID, "input"));
|
||||||
|
|
||||||
assertThat(e.getMessage(), is("Failed to execute script."));
|
assertThat(e.getMessage(), is("Failed to execute script."));
|
||||||
assertThat(e.getCause(), instanceOf(ScriptException.class));
|
assertThat(e.getCause(), instanceOf(ScriptException.class));
|
||||||
assertThat(e.getCause().getMessage(), is("exception"));
|
assertThat(e.getCause().getMessage(), is("exception"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invalidConfigurationTypeThrowsTransformationException() {
|
||||||
|
TransformationException e = assertThrows(TransformationException.class,
|
||||||
|
() -> service.transform(SCRIPT_LANGUAGE + ":" + INVALID_SCRIPT_UID, "input"));
|
||||||
|
|
||||||
|
assertThat(e.getMessage(), is("Configuration does not have correct type 'script' but 'invalid'."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ public class DSLScriptEngine implements javax.script.ScriptEngine {
|
|||||||
private static final Map<String, String> IMPLICIT_VARS = Map.of("command",
|
private static final Map<String, String> IMPLICIT_VARS = Map.of("command",
|
||||||
ScriptJvmModelInferrer.VAR_RECEIVED_COMMAND, "state", ScriptJvmModelInferrer.VAR_NEW_STATE, "newState",
|
ScriptJvmModelInferrer.VAR_RECEIVED_COMMAND, "state", ScriptJvmModelInferrer.VAR_NEW_STATE, "newState",
|
||||||
ScriptJvmModelInferrer.VAR_NEW_STATE, "oldState", ScriptJvmModelInferrer.VAR_PREVIOUS_STATE,
|
ScriptJvmModelInferrer.VAR_NEW_STATE, "oldState", ScriptJvmModelInferrer.VAR_PREVIOUS_STATE,
|
||||||
"triggeringItem", ScriptJvmModelInferrer.VAR_TRIGGERING_ITEM);
|
"triggeringItem", ScriptJvmModelInferrer.VAR_TRIGGERING_ITEM, "input", ScriptJvmModelInferrer.VAR_INPUT);
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(DSLScriptEngine.class);
|
private final Logger logger = LoggerFactory.getLogger(DSLScriptEngine.class);
|
||||||
|
|
||||||
|
@ -40,6 +40,9 @@ class ScriptJvmModelInferrer extends AbstractModelInferrer {
|
|||||||
|
|
||||||
static private final Logger logger = LoggerFactory.getLogger(ScriptJvmModelInferrer)
|
static private final Logger logger = LoggerFactory.getLogger(ScriptJvmModelInferrer)
|
||||||
|
|
||||||
|
/** Variable name for the input string in a "script transformation" or "script profile" */
|
||||||
|
public static final String VAR_INPUT = "input";
|
||||||
|
|
||||||
/** Variable name for the item in a "state triggered" or "command triggered" rule */
|
/** Variable name for the item in a "state triggered" or "command triggered" rule */
|
||||||
public static final String VAR_TRIGGERING_ITEM = "triggeringItem";
|
public static final String VAR_TRIGGERING_ITEM = "triggeringItem";
|
||||||
|
|
||||||
@ -121,6 +124,8 @@ class ScriptJvmModelInferrer extends AbstractModelInferrer {
|
|||||||
|
|
||||||
members += script.toMethod("_script", null) [
|
members += script.toMethod("_script", null) [
|
||||||
static = true
|
static = true
|
||||||
|
val inputTypeRef = script.newTypeRef(String)
|
||||||
|
parameters += script.toParameter(VAR_INPUT, inputTypeRef)
|
||||||
val itemTypeRef = script.newTypeRef(Item)
|
val itemTypeRef = script.newTypeRef(Item)
|
||||||
parameters += script.toParameter(VAR_TRIGGERING_ITEM, itemTypeRef)
|
parameters += script.toParameter(VAR_TRIGGERING_ITEM, itemTypeRef)
|
||||||
val itemNameRef = script.newTypeRef(String)
|
val itemNameRef = script.newTypeRef(String)
|
||||||
|
Loading…
Reference in New Issue
Block a user