diff --git a/connectors/citrus-sql/src/main/java/org/citrusframework/sql/xml/Sql.java b/connectors/citrus-sql/src/main/java/org/citrusframework/sql/xml/Sql.java index 9b66f5e83a..becfccc739 100644 --- a/connectors/citrus-sql/src/main/java/org/citrusframework/sql/xml/Sql.java +++ b/connectors/citrus-sql/src/main/java/org/citrusframework/sql/xml/Sql.java @@ -350,9 +350,7 @@ public List getValues() { } @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = { - "value" - }) + @XmlType(name = "") public static class Script { @XmlValue diff --git a/endpoints/citrus-camel/src/main/java/org/citrusframework/camel/xml/ControlBus.java b/endpoints/citrus-camel/src/main/java/org/citrusframework/camel/xml/ControlBus.java index 3078779ca1..c48e2f0395 100644 --- a/endpoints/citrus-camel/src/main/java/org/citrusframework/camel/xml/ControlBus.java +++ b/endpoints/citrus-camel/src/main/java/org/citrusframework/camel/xml/ControlBus.java @@ -64,9 +64,7 @@ public void setResult(String result) { } @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = { - "type" - }) + @XmlType(name = "") public static class Language { @XmlAttribute protected String type = "simple"; diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/GroovyShellUtils.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/GroovyShellUtils.java index dc71779ded..b583b265bd 100644 --- a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/GroovyShellUtils.java +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/GroovyShellUtils.java @@ -85,7 +85,10 @@ public static T run(ImportCustomizer ic, Object delegate, String scriptCode, ((GroovyScript) script).setDelegate(delegate); } - ((GroovyScript) script).setCitrusFramework(citrus); + if (citrus != null) { + ((GroovyScript) script).setCitrusFramework(citrus); + } + ((GroovyScript) script).setContext(context); } diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/GroovySupport.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/GroovySupport.java index c764c62c0d..e55e59a278 100644 --- a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/GroovySupport.java +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/GroovySupport.java @@ -21,6 +21,7 @@ import org.citrusframework.context.TestContext; import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.script.GroovyActionBuilder; import org.citrusframework.spi.Resource; import org.citrusframework.util.FileUtils; import org.codehaus.groovy.control.customizers.ImportCustomizer; @@ -41,6 +42,14 @@ public static GroovySupport groovy() { return new GroovySupport(); } + /** + * Delegates to Groovy test actions. + * @return + */ + public GroovyActionBuilder actions() { + return GroovyActionBuilder.groovy(); + } + /** * Loads given Groovy script and return the constructed Java object. * @param script diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/beans/BeansConfiguration.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/beans/BeansConfiguration.java index 1443ada0d0..1b8bb663fc 100644 --- a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/beans/BeansConfiguration.java +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/beans/BeansConfiguration.java @@ -20,19 +20,36 @@ import java.util.Locale; import groovy.lang.Closure; +import groovy.lang.DelegatesTo; import groovy.lang.GroovyObjectSupport; import groovy.lang.GroovyRuntimeException; import groovy.lang.MissingMethodException; import org.citrusframework.Citrus; +import org.citrusframework.CitrusContext; import org.citrusframework.exceptions.CitrusRuntimeException; import org.citrusframework.message.DefaultMessageQueue; +import org.citrusframework.spi.ReferenceResolver; public class BeansConfiguration extends GroovyObjectSupport { - private final Citrus citrus; + private final ReferenceResolver referenceResolver; public BeansConfiguration(Citrus citrus) { - this.citrus = citrus; + this(citrus.getCitrusContext()); + } + + public BeansConfiguration(CitrusContext citrusContext) { + this(citrusContext.getReferenceResolver()); + } + + public BeansConfiguration(ReferenceResolver referenceResolver) { + this.referenceResolver = referenceResolver; + } + + public void beans(@DelegatesTo(BeansConfiguration.class) Closure callable) { + callable.setResolveStrategy(Closure.DELEGATE_FIRST); + callable.setDelegate(this); + callable.call(); } public void bean(Class type) { @@ -41,18 +58,31 @@ public void bean(Class type) { public void bean(String name, Class type) { try { - citrus.getCitrusContext().bind(name, type.getConstructor().newInstance()); + referenceResolver.bind(name, type.getConstructor().newInstance()); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new CitrusRuntimeException(String.format("Failed to instantiate bean of type '%s' - no default constructor available", type)); + } + } + + public void bean(String name, Class type, Closure callable) { + try { + Object bean = type.getDeclaredConstructor().newInstance(); + callable.setResolveStrategy(Closure.DELEGATE_ONLY); + callable.setDelegate(bean); + callable.call(); + + referenceResolver.bind(name, bean); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new CitrusRuntimeException(String.format("Failed to instantiate bean of type '%s' - no default constructor available", type)); } } public void queue(String name) { - citrus.getCitrusContext().bind(name, new DefaultMessageQueue(name)); + referenceResolver.bind(name, new DefaultMessageQueue(name)); } public void propertyMissing(String name, Object value) { - citrus.getCitrusContext().bind(name, value); + referenceResolver.bind(name, value); } public Object methodMissing(String name, Object argLine) { @@ -71,7 +101,7 @@ public Object methodMissing(String name, Object argLine) { closure.setDelegate(bean); closure.call(); - citrus.getCitrusContext().bind(name, bean); + referenceResolver.bind(name, bean); return bean; } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) { throw new GroovyRuntimeException(String.format("Failed to instantiate bean of type '%s'", type), e); @@ -82,7 +112,7 @@ public Object methodMissing(String name, Object argLine) { closure.setResolveStrategy(Closure.DELEGATE_ONLY); Object bean = closure.call(); - citrus.getCitrusContext().bind(name, bean); + referenceResolver.bind(name, bean); } } diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/beans/QueueConfiguration.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/beans/QueueConfiguration.java index e9232e5116..cfc1618544 100644 --- a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/beans/QueueConfiguration.java +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/beans/QueueConfiguration.java @@ -17,17 +17,27 @@ package org.citrusframework.groovy.dsl.configuration.beans; import org.citrusframework.Citrus; +import org.citrusframework.CitrusContext; import org.citrusframework.message.DefaultMessageQueue; +import org.citrusframework.spi.ReferenceResolver; public class QueueConfiguration { - private final Citrus citrus; + private final ReferenceResolver referenceResolver; public QueueConfiguration(Citrus citrus) { - this.citrus = citrus; + this(citrus.getCitrusContext()); + } + + public QueueConfiguration(CitrusContext citrusContext) { + this(citrusContext.getReferenceResolver()); + } + + public QueueConfiguration(ReferenceResolver referenceResolver) { + this.referenceResolver = referenceResolver; } public void queue(String name) { - citrus.getCitrusContext().bind(name, new DefaultMessageQueue(name)); + referenceResolver.bind(name, new DefaultMessageQueue(name)); } } diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/endpoints/EndpointConfigurationScript.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/endpoints/EndpointConfigurationScript.java index 04acdc1503..73e802f302 100644 --- a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/endpoints/EndpointConfigurationScript.java +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/dsl/configuration/endpoints/EndpointConfigurationScript.java @@ -17,34 +17,54 @@ package org.citrusframework.groovy.dsl.configuration.endpoints; import org.citrusframework.Citrus; +import org.citrusframework.CitrusContext; import org.citrusframework.common.InitializingPhase; import org.citrusframework.context.TestContext; import org.citrusframework.endpoint.Endpoint; import org.citrusframework.groovy.dsl.GroovyShellUtils; +import org.citrusframework.spi.ReferenceResolver; +import org.citrusframework.spi.ReferenceResolverAware; import org.codehaus.groovy.control.customizers.ImportCustomizer; -public class EndpointConfigurationScript { +public class EndpointConfigurationScript implements ReferenceResolverAware { - private final Citrus citrus; + private ReferenceResolver referenceResolver; private final String script; public EndpointConfigurationScript(String script, Citrus citrus) { + this(script, citrus.getCitrusContext()); + } + + public EndpointConfigurationScript(String script, CitrusContext citrusContext) { + this(script, citrusContext.getReferenceResolver()); + } + + public EndpointConfigurationScript(String script, ReferenceResolver referenceResolver) { + this.script = script; + this.referenceResolver = referenceResolver; + } + + public EndpointConfigurationScript(String script) { this.script = script; - this.citrus = citrus; } public void execute(TestContext context) { EndpointsConfiguration configuration = new EndpointsConfiguration(); ImportCustomizer ic = new ImportCustomizer(); - GroovyShellUtils.run(ic, configuration, context.replaceDynamicContentInString(script), citrus, context); + GroovyShellUtils.run(ic, configuration, context.replaceDynamicContentInString(script), null, context); configuration.getEndpoints().forEach(endpoint -> { onCreate(endpoint); if (endpoint instanceof InitializingPhase) { ((InitializingPhase) endpoint).initialize(); } - citrus.getCitrusContext().bind(endpoint.getName(), endpoint); + + if (referenceResolver != null) { + referenceResolver.bind(endpoint.getName(), endpoint); + } else { + context.getReferenceResolver().bind(endpoint.getName(), endpoint); + } }); } @@ -54,4 +74,9 @@ public void execute(TestContext context) { */ protected void onCreate(Endpoint endpoint) { } + + @Override + public void setReferenceResolver(ReferenceResolver referenceResolver) { + this.referenceResolver = referenceResolver; + } } diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/xml/Groovy.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/xml/Groovy.java index bd8d2d9972..ea608fa2e9 100644 --- a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/xml/Groovy.java +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/xml/Groovy.java @@ -16,46 +16,157 @@ package org.citrusframework.groovy.xml; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; -import jakarta.xml.bind.annotation.XmlValue; - +import jakarta.xml.bind.annotation.XmlType; +import org.citrusframework.AbstractTestActionBuilder; +import org.citrusframework.TestAction; import org.citrusframework.TestActionBuilder; +import org.citrusframework.TestActor; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.script.CreateBeansAction; +import org.citrusframework.script.CreateEndpointsAction; import org.citrusframework.script.GroovyAction; +import org.citrusframework.spi.ReferenceResolver; +import org.citrusframework.spi.ReferenceResolverAware; +import org.citrusframework.util.StringUtils; @XmlRootElement(name = "groovy") -public class Groovy implements TestActionBuilder { +public class Groovy implements TestActionBuilder, ReferenceResolverAware { - private final GroovyAction.Builder builder = new GroovyAction.Builder(); + private AbstractTestActionBuilder builder; - @XmlValue - public Groovy setScript(String value) { - if (value.length() > 0) { - builder.script(value); - } + private String description; + private String actor; + + private ReferenceResolver referenceResolver; + + @XmlElement + public Groovy setDescription(String value) { + this.description = value; return this; } - @XmlAttribute(name = "file") - public Groovy setFile(String file) { - builder.scriptResourcePath(file); + @XmlAttribute(name = "actor") + public Groovy setActor(String actor) { + this.actor = actor; return this; } - @XmlAttribute(name = "script-template") - public Groovy setTemplate(String template) { - builder.template(template); + @XmlElement + public Groovy setScript(Script script) { + GroovyAction.Builder builder = new GroovyAction.Builder(); + + if (StringUtils.hasText(script.getValue())) { + builder.script(script.getValue()); + } + + if (script.getFile() != null) { + builder.scriptResourcePath(script.getFile()); + } + + if (script.getTemplate() != null) { + builder.template(script.getTemplate()); + } + + builder.useScriptTemplate(script.isUseScriptTemplate()); + + this.builder = builder; return this; } - @XmlAttribute(name = "use-script-template") - public Groovy setUseScriptTemplate(boolean enabled) { - builder.useScriptTemplate(enabled); + @XmlElement + public Groovy setEndpoints(Endpoints endpoints) { + CreateEndpointsAction.Builder builder = new CreateEndpointsAction.Builder(); + + if (StringUtils.hasText(endpoints.getScript().getValue())) { + builder.script(endpoints.getScript().getValue()); + } + + if (endpoints.getScript().getFile() != null) { + builder.scriptResourcePath(endpoints.getScript().getFile()); + } + + this.builder = builder; + return this; + } + + @XmlElement + public Groovy setBeans(Beans beans) { + CreateBeansAction.Builder builder = new CreateBeansAction.Builder(); + + if (StringUtils.hasText(beans.getScript().getValue())) { + builder.script(beans.getScript().getValue()); + } + + if (beans.getScript().getFile() != null) { + builder.scriptResourcePath(beans.getScript().getFile()); + } + + this.builder = builder; return this; } @Override - public GroovyAction build() { + public TestAction build() { + if (builder == null) { + throw new CitrusRuntimeException("Missing Groovy action - please provide proper action details"); + } + + if (builder instanceof ReferenceResolverAware referenceResolverAware) { + referenceResolverAware.setReferenceResolver(referenceResolver); + } + + builder.description(description); + + if (referenceResolver != null) { + if (actor != null) { + builder.actor(referenceResolver.resolve(actor, TestActor.class)); + } + } + return builder.build(); } + + @Override + public void setReferenceResolver(ReferenceResolver referenceResolver) { + this.referenceResolver = referenceResolver; + } + + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(name = "", propOrder = { + "script" + }) + public static class Endpoints { + @XmlElement + private Script script; + + public void setScript(Script script) { + this.script = script; + } + + public Script getScript() { + return script; + } + } + + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(name = "", propOrder = { + "script" + }) + public static class Beans { + @XmlElement + private Script script; + + public void setScript(Script script) { + this.script = script; + } + + public Script getScript() { + return script; + } + } } diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/xml/Script.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/xml/Script.java new file mode 100644 index 0000000000..764f2545bd --- /dev/null +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/xml/Script.java @@ -0,0 +1,72 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citrusframework.groovy.xml; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlValue; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +public class Script { + + @XmlValue + private String value; + + @XmlAttribute + private String file; + + @XmlAttribute(name = "use-script-template") + private boolean useScriptTemplate = true; + + @XmlAttribute(name = "script-template") + private String template; + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getTemplate() { + return template; + } + + public void setTemplate(String template) { + this.template = template; + } + + public boolean isUseScriptTemplate() { + return useScriptTemplate; + } + + public void setUseScriptTemplate(boolean useScriptTemplate) { + this.useScriptTemplate = useScriptTemplate; + } +} diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/yaml/Groovy.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/yaml/Groovy.java index 42a73c04bd..207dcd690b 100644 --- a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/yaml/Groovy.java +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/yaml/Groovy.java @@ -16,33 +16,112 @@ package org.citrusframework.groovy.yaml; +import org.citrusframework.AbstractTestActionBuilder; +import org.citrusframework.TestAction; import org.citrusframework.TestActionBuilder; +import org.citrusframework.TestActionContainerBuilder; +import org.citrusframework.TestActor; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.script.CreateBeansAction; +import org.citrusframework.script.CreateEndpointsAction; import org.citrusframework.script.GroovyAction; +import org.citrusframework.spi.ReferenceResolver; +import org.citrusframework.spi.ReferenceResolverAware; -public class Groovy implements TestActionBuilder { +public class Groovy implements TestActionBuilder, ReferenceResolverAware { - private final GroovyAction.Builder builder = new GroovyAction.Builder(); + private AbstractTestActionBuilder builder; - public void setScript(String value) { - if (value.length() > 0) { - builder.script(value); - } + private String description; + private String actor; + + private ReferenceResolver referenceResolver; + + public void setDescription(String value) { + this.description = description; } - public void setFile(String file) { - builder.scriptResourcePath(file); + public void setActor(String actor) { + this.actor = actor; } - public void setScriptTemplate(String template) { - builder.template(template); + public void setScript(Script script) { + GroovyAction.Builder builder = new GroovyAction.Builder(); + + if (script.getValue() != null) { + builder.script(script.getValue()); + } + + if (script.getFile() != null) { + builder.scriptResourcePath(script.getFile()); + } + + if (script.getTemplate() != null) { + builder.template(script.getTemplate()); + } + + builder.useScriptTemplate(script.isUseScriptTemplate()); + + this.builder = builder; } - public void setUseScriptTemplate(boolean enabled) { - builder.useScriptTemplate(enabled); + public void setEndpoints(Script script) { + CreateEndpointsAction.Builder builder = new CreateEndpointsAction.Builder(); + + if (script.getValue() != null) { + builder.script(script.getValue()); + } + + if (script.getFile() != null) { + builder.scriptResourcePath(script.getFile()); + } + + this.builder = builder; + } + + public void setBeans(Script script) { + CreateBeansAction.Builder builder = new CreateBeansAction.Builder(); + + if (script.getValue() != null) { + builder.script(script.getValue()); + } + + if (script.getFile() != null) { + builder.scriptResourcePath(script.getFile()); + } + + this.builder = builder; } @Override - public GroovyAction build() { + public TestAction build() { + if (builder == null) { + throw new CitrusRuntimeException("Missing Groovy action - please provide proper action details"); + } + + if (builder instanceof TestActionContainerBuilder) { + ((TestActionContainerBuilder) builder).getActions().stream() + .filter(action -> action instanceof ReferenceResolverAware) + .forEach(action -> ((ReferenceResolverAware) action).setReferenceResolver(referenceResolver)); + } + + if (builder instanceof ReferenceResolverAware) { + ((ReferenceResolverAware) builder).setReferenceResolver(referenceResolver); + } + + builder.description(description); + + if (referenceResolver != null) { + if (actor != null) { + builder.actor(referenceResolver.resolve(actor, TestActor.class)); + } + } + return builder.build(); } + + @Override + public void setReferenceResolver(ReferenceResolver referenceResolver) { + this.referenceResolver = referenceResolver; + } } diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/yaml/Script.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/yaml/Script.java new file mode 100644 index 0000000000..7221f945b6 --- /dev/null +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/groovy/yaml/Script.java @@ -0,0 +1,64 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citrusframework.groovy.yaml; + +public class Script { + + protected String file; + + protected boolean useScriptTemplate = true; + + protected String template; + + protected String value; + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public void setScript(String script) { + this.value = script; + } + + public String getTemplate() { + return template; + } + + public void setTemplate(String template) { + this.template = template; + } + + public boolean isUseScriptTemplate() { + return useScriptTemplate; + } + + public void setUseScriptTemplate(boolean useScriptTemplate) { + this.useScriptTemplate = useScriptTemplate; + } +} diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/script/CreateBeansAction.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/script/CreateBeansAction.java new file mode 100644 index 0000000000..b21b9d9954 --- /dev/null +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/script/CreateBeansAction.java @@ -0,0 +1,132 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citrusframework.script; + +import java.io.IOException; +import java.nio.charset.Charset; + +import org.citrusframework.AbstractTestActionBuilder; +import org.citrusframework.actions.AbstractTestAction; +import org.citrusframework.context.TestContext; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.groovy.dsl.GroovyShellUtils; +import org.citrusframework.groovy.dsl.configuration.beans.BeansConfiguration; +import org.citrusframework.spi.Resource; +import org.citrusframework.util.FileUtils; +import org.citrusframework.util.StringUtils; + +public class CreateBeansAction extends AbstractTestAction { + + /** Inline groovy script */ + private final String script; + + /** External script file resource path */ + private final String scriptResourcePath; + + /** + * Default constructor. + */ + public CreateBeansAction(Builder builder) { + super("groovy-beans", builder); + + this.script = builder.script; + this.scriptResourcePath = builder.scriptResourcePath; + } + + @Override + public void doExecute(TestContext context) { + try { + if (!StringUtils.hasText(script) && scriptResourcePath == null) { + throw new CitrusRuntimeException("Neither inline script nor " + + "external script resource is defined. Unable to execute groovy script."); + } + + String resolvedScript = StringUtils.hasText(script) ? script.trim() : FileUtils.readToString(FileUtils.getFileResource(scriptResourcePath, context)); + resolvedScript = context.replaceDynamicContentInString(resolvedScript.trim()); + + GroovyShellUtils.run(new BeansConfiguration(context.getReferenceResolver()), resolvedScript, null, context); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to load bean configuration script", e); + } + } + + public String getScript() { + return script; + } + + public String getScriptResourcePath() { + return scriptResourcePath; + } + + /** + * Action builder. + */ + public static final class Builder extends AbstractTestActionBuilder { + + private String script; + private String scriptResourcePath; + + /** + * Sets the Groovy script to execute. + * @param script + * @return + */ + public Builder script(String script) { + this.script = script; + return this; + } + + /** + * Sets the Groovy script to execute. + * @param scriptResource + * @return + */ + public Builder script(Resource scriptResource) { + return script(scriptResource, FileUtils.getDefaultCharset()); + } + + /** + * Sets the Groovy script to execute. + * @param scriptResource + * @param charset + * @return + */ + public Builder script(Resource scriptResource, Charset charset) { + try { + this.script = FileUtils.readToString(scriptResource, charset); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read script resource file", e); + } + return this; + } + + /** + * Sets the Groovy script to execute. + * @param scriptResourcePath + * @return + */ + public Builder scriptResourcePath(String scriptResourcePath) { + this.scriptResourcePath = scriptResourcePath; + return this; + } + + @Override + public CreateBeansAction build() { + return new CreateBeansAction(this); + } + } +} diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/script/CreateEndpointsAction.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/script/CreateEndpointsAction.java new file mode 100644 index 0000000000..7a6b67e334 --- /dev/null +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/script/CreateEndpointsAction.java @@ -0,0 +1,142 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citrusframework.script; + +import java.io.IOException; +import java.nio.charset.Charset; + +import org.citrusframework.AbstractTestActionBuilder; +import org.citrusframework.actions.AbstractTestAction; +import org.citrusframework.context.TestContext; +import org.citrusframework.endpoint.Endpoint; +import org.citrusframework.exceptions.CitrusRuntimeException; +import org.citrusframework.groovy.dsl.configuration.endpoints.EndpointConfigurationScript; +import org.citrusframework.spi.Resource; +import org.citrusframework.util.FileUtils; +import org.citrusframework.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CreateEndpointsAction extends AbstractTestAction { + + /** Logger */ + private static final Logger logger = LoggerFactory.getLogger(CreateEndpointsAction.class); + + /** Inline groovy script */ + private final String script; + + /** External script file resource path */ + private final String scriptResourcePath; + + /** + * Default constructor. + */ + public CreateEndpointsAction(Builder builder) { + super("groovy-endpoints", builder); + + this.script = builder.script; + this.scriptResourcePath = builder.scriptResourcePath; + } + + @Override + public void doExecute(TestContext context) { + try { + if (!StringUtils.hasText(script) && scriptResourcePath == null) { + throw new CitrusRuntimeException("Neither inline script nor " + + "external script resource is defined. Unable to execute groovy script."); + } + + String resolvedScript = StringUtils.hasText(script) ? script.trim() : FileUtils.readToString(FileUtils.getFileResource(scriptResourcePath, context)); + resolvedScript = context.replaceDynamicContentInString(resolvedScript.trim()); + + new EndpointConfigurationScript(resolvedScript, context.getReferenceResolver()) { + @Override + protected void onCreate(Endpoint endpoint) { + logger.info("Creating endpoint from script configuration: %s".formatted(endpoint.getName())); + } + }.execute(context); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to load endpoint configuration script", e); + } + } + + public String getScript() { + return script; + } + + public String getScriptResourcePath() { + return scriptResourcePath; + } + + /** + * Action builder. + */ + public static final class Builder extends AbstractTestActionBuilder { + + private String script; + private String scriptResourcePath; + + /** + * Sets the Groovy script to execute. + * @param script + * @return + */ + public Builder script(String script) { + this.script = script; + return this; + } + + /** + * Sets the Groovy script to execute. + * @param scriptResource + * @return + */ + public Builder script(Resource scriptResource) { + return script(scriptResource, FileUtils.getDefaultCharset()); + } + + /** + * Sets the Groovy script to execute. + * @param scriptResource + * @param charset + * @return + */ + public Builder script(Resource scriptResource, Charset charset) { + try { + this.script = FileUtils.readToString(scriptResource, charset); + } catch (IOException e) { + throw new CitrusRuntimeException("Failed to read script resource file", e); + } + return this; + } + + /** + * Sets the Groovy script to execute. + * @param scriptResourcePath + * @return + */ + public Builder scriptResourcePath(String scriptResourcePath) { + this.scriptResourcePath = scriptResourcePath; + return this; + } + + @Override + public CreateEndpointsAction build() { + return new CreateEndpointsAction(this); + } + } +} diff --git a/runtime/citrus-groovy/src/main/java/org/citrusframework/script/GroovyActionBuilder.java b/runtime/citrus-groovy/src/main/java/org/citrusframework/script/GroovyActionBuilder.java new file mode 100644 index 0000000000..79f617ea37 --- /dev/null +++ b/runtime/citrus-groovy/src/main/java/org/citrusframework/script/GroovyActionBuilder.java @@ -0,0 +1,98 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citrusframework.script; + +import jakarta.annotation.Nullable; +import org.citrusframework.TestAction; +import org.citrusframework.TestActionBuilder; +import org.citrusframework.spi.ReferenceResolver; +import org.citrusframework.spi.ReferenceResolverAware; +import org.citrusframework.util.ObjectHelper; + +public class GroovyActionBuilder implements TestActionBuilder.DelegatingTestActionBuilder, ReferenceResolverAware { + + private TestActionBuilder delegate; + + private GroovyActionBuilder() { + // hide constructor of builder class - use static entrance method instead + } + + public static GroovyActionBuilder groovy() { + return new GroovyActionBuilder(); + } + + /** + * Run Groovy script. + * @return + */ + public GroovyAction.Builder run() { + GroovyAction.Builder builder = new GroovyAction.Builder(); + this.delegate = builder; + return builder; + } + + /** + * Create endpoints from Groovy script. + * @return + */ + public CreateEndpointsAction.Builder endpoints() { + CreateEndpointsAction.Builder builder = new CreateEndpointsAction.Builder(); + this.delegate = builder; + return builder; + } + + /** + * Create beans in registry from Groovy script. + * @return + */ + public CreateBeansAction.Builder beans() { + CreateBeansAction.Builder builder = new CreateBeansAction.Builder(); + this.delegate = builder; + return builder; + } + + /** + * Sets the bean reference resolver. + * @param referenceResolver + */ + public GroovyActionBuilder withReferenceResolver(ReferenceResolver referenceResolver) { + setReferenceResolver(referenceResolver); + return this; + } + + @Override + public TestAction build() { + ObjectHelper.assertNotNull(delegate, "Missing delegate action to build"); + return delegate.build(); + } + + @Override + public TestActionBuilder getDelegate() { + return delegate; + } + + /** + * Specifies the referenceResolver. + */ + @Override + public void setReferenceResolver(@Nullable ReferenceResolver referenceResolver) { + if (referenceResolver != null + && delegate instanceof ReferenceResolverAware referenceResolverAware) { + referenceResolverAware.setReferenceResolver(referenceResolver); + } + } +} diff --git a/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/xml/CreateBeansTest.java b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/xml/CreateBeansTest.java new file mode 100644 index 0000000000..461957763e --- /dev/null +++ b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/xml/CreateBeansTest.java @@ -0,0 +1,49 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citrusframework.groovy.xml; + +import org.citrusframework.TestCase; +import org.citrusframework.TestCaseMetaInfo; +import org.citrusframework.script.CreateBeansAction; +import org.citrusframework.validation.DefaultTextEqualsMessageValidator; +import org.citrusframework.xml.XmlTestLoader; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class CreateBeansTest extends AbstractXmlActionTest { + + @Test + public void shouldLoadGroovyActions() { + XmlTestLoader testLoader = createTestLoader("classpath:org/citrusframework/groovy/xml/create-beans-test.xml"); + + Assert.assertFalse(context.getReferenceResolver().isResolvable(DefaultTextEqualsMessageValidator.class)); + + testLoader.load(); + TestCase result = testLoader.getTestCase(); + Assert.assertEquals(result.getName(), "CreateBeansTest"); + Assert.assertEquals(result.getMetaInfo().getAuthor(), "Christoph"); + Assert.assertEquals(result.getMetaInfo().getStatus(), TestCaseMetaInfo.Status.FINAL); + Assert.assertEquals(result.getActionCount(), 1L); + Assert.assertEquals(result.getTestAction(0).getClass(), CreateBeansAction.class); + + CreateBeansAction action = (CreateBeansAction) result.getTestAction(0); + Assert.assertNull(action.getScriptResourcePath()); + Assert.assertNotNull(action.getScript().trim()); + + Assert.assertTrue(context.getReferenceResolver().isResolvable(DefaultTextEqualsMessageValidator.class)); + } +} diff --git a/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/xml/CreateEndpointsTest.java b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/xml/CreateEndpointsTest.java new file mode 100644 index 0000000000..4a938d4c89 --- /dev/null +++ b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/xml/CreateEndpointsTest.java @@ -0,0 +1,51 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citrusframework.groovy.xml; + +import org.citrusframework.TestCase; +import org.citrusframework.TestCaseMetaInfo; +import org.citrusframework.script.CreateEndpointsAction; +import org.citrusframework.xml.XmlTestLoader; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class CreateEndpointsTest extends AbstractXmlActionTest { + + @Test + public void shouldLoadGroovyActions() { + XmlTestLoader testLoader = createTestLoader("classpath:org/citrusframework/groovy/xml/create-endpoints-test.xml"); + + testLoader.load(); + TestCase result = testLoader.getTestCase(); + Assert.assertEquals(result.getName(), "CreateEndpointsTest"); + Assert.assertEquals(result.getMetaInfo().getAuthor(), "Christoph"); + Assert.assertEquals(result.getMetaInfo().getStatus(), TestCaseMetaInfo.Status.FINAL); + Assert.assertEquals(result.getActionCount(), 2L); + Assert.assertEquals(result.getTestAction(0).getClass(), CreateEndpointsAction.class); + + int actionIndex = 0; + + CreateEndpointsAction action = (CreateEndpointsAction) result.getTestAction(actionIndex++); + Assert.assertNull(action.getScriptResourcePath()); + Assert.assertNotNull(action.getScript().trim()); + + action = (CreateEndpointsAction) result.getTestAction(actionIndex); + Assert.assertNotNull(action.getScriptResourcePath()); + Assert.assertEquals(action.getScriptResourcePath(), "classpath:org/citrusframework/groovy/dsl/endpoints.groovy"); + Assert.assertNull(action.getScript()); + } +} diff --git a/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/xml/GroovyTest.java b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/xml/GroovyTest.java index 86ac5a86e4..dd250cb08c 100644 --- a/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/xml/GroovyTest.java +++ b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/xml/GroovyTest.java @@ -27,7 +27,7 @@ public class GroovyTest extends AbstractXmlActionTest { @Test - public void shouldLoadGroovy() { + public void shouldLoadGroovyActions() { XmlTestLoader testLoader = createTestLoader("classpath:org/citrusframework/groovy/xml/groovy-test.xml"); testLoader.load(); diff --git a/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/yaml/CreateBeansTest.java b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/yaml/CreateBeansTest.java new file mode 100644 index 0000000000..49f919e471 --- /dev/null +++ b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/yaml/CreateBeansTest.java @@ -0,0 +1,49 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citrusframework.groovy.yaml; + +import org.citrusframework.TestCase; +import org.citrusframework.TestCaseMetaInfo; +import org.citrusframework.script.CreateBeansAction; +import org.citrusframework.validation.DefaultTextEqualsMessageValidator; +import org.citrusframework.yaml.YamlTestLoader; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class CreateBeansTest extends AbstractYamlActionTest { + + @Test + public void shouldLoadGroovyActions() { + YamlTestLoader testLoader = createTestLoader("classpath:org/citrusframework/groovy/yaml/create-beans-test.yaml"); + + Assert.assertFalse(context.getReferenceResolver().isResolvable(DefaultTextEqualsMessageValidator.class)); + + testLoader.load(); + TestCase result = testLoader.getTestCase(); + Assert.assertEquals(result.getName(), "CreateBeansTest"); + Assert.assertEquals(result.getMetaInfo().getAuthor(), "Christoph"); + Assert.assertEquals(result.getMetaInfo().getStatus(), TestCaseMetaInfo.Status.FINAL); + Assert.assertEquals(result.getActionCount(), 1L); + Assert.assertEquals(result.getTestAction(0).getClass(), CreateBeansAction.class); + + CreateBeansAction action = (CreateBeansAction) result.getTestAction(0); + Assert.assertNull(action.getScriptResourcePath()); + Assert.assertNotNull(action.getScript().trim()); + + Assert.assertTrue(context.getReferenceResolver().isResolvable(DefaultTextEqualsMessageValidator.class)); + } +} diff --git a/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/yaml/CreateEndpointsTest.java b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/yaml/CreateEndpointsTest.java new file mode 100644 index 0000000000..940e80fd4a --- /dev/null +++ b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/yaml/CreateEndpointsTest.java @@ -0,0 +1,51 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citrusframework.groovy.yaml; + +import org.citrusframework.TestCase; +import org.citrusframework.TestCaseMetaInfo; +import org.citrusframework.script.CreateEndpointsAction; +import org.citrusframework.yaml.YamlTestLoader; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class CreateEndpointsTest extends AbstractYamlActionTest { + + @Test + public void shouldLoadGroovyActions() { + YamlTestLoader testLoader = createTestLoader("classpath:org/citrusframework/groovy/yaml/create-endpoints-test.yaml"); + + testLoader.load(); + TestCase result = testLoader.getTestCase(); + Assert.assertEquals(result.getName(), "CreateEndpointsTest"); + Assert.assertEquals(result.getMetaInfo().getAuthor(), "Christoph"); + Assert.assertEquals(result.getMetaInfo().getStatus(), TestCaseMetaInfo.Status.FINAL); + Assert.assertEquals(result.getActionCount(), 2L); + Assert.assertEquals(result.getTestAction(0).getClass(), CreateEndpointsAction.class); + + int actionIndex = 0; + + CreateEndpointsAction action = (CreateEndpointsAction) result.getTestAction(actionIndex++); + Assert.assertNull(action.getScriptResourcePath()); + Assert.assertNotNull(action.getScript().trim()); + + action = (CreateEndpointsAction) result.getTestAction(actionIndex++); + Assert.assertNotNull(action.getScriptResourcePath()); + Assert.assertEquals(action.getScriptResourcePath(), "classpath:org/citrusframework/groovy/dsl/endpoints.groovy"); + Assert.assertNull(action.getScript()); + } +} diff --git a/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/yaml/GroovyTest.java b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/yaml/GroovyTest.java index d4fcce2bd7..b6b4bc8128 100644 --- a/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/yaml/GroovyTest.java +++ b/runtime/citrus-groovy/src/test/java/org/citrusframework/groovy/yaml/GroovyTest.java @@ -27,7 +27,7 @@ public class GroovyTest extends AbstractYamlActionTest { @Test - public void shouldLoadGroovy() { + public void shouldLoadGroovyActions() { YamlTestLoader testLoader = createTestLoader("classpath:org/citrusframework/groovy/yaml/groovy-test.yaml"); testLoader.load(); diff --git a/runtime/citrus-groovy/src/test/resources/org/citrusframework/groovy/xml/create-beans-test.xml b/runtime/citrus-groovy/src/test/resources/org/citrusframework/groovy/xml/create-beans-test.xml new file mode 100644 index 0000000000..3add9c4df3 --- /dev/null +++ b/runtime/citrus-groovy/src/test/resources/org/citrusframework/groovy/xml/create-beans-test.xml @@ -0,0 +1,32 @@ + + + + Sample test in XML + + + + + + + + diff --git a/runtime/citrus-groovy/src/test/resources/org/citrusframework/groovy/xml/create-endpoints-test.xml b/runtime/citrus-groovy/src/test/resources/org/citrusframework/groovy/xml/create-endpoints-test.xml new file mode 100644 index 0000000000..d11f2fb3af --- /dev/null +++ b/runtime/citrus-groovy/src/test/resources/org/citrusframework/groovy/xml/create-endpoints-test.xml @@ -0,0 +1,46 @@ + + + + Sample test in XML + + + + + + + + + + - - println 'Hello Citrus' + + - - + + - + +