Skip to content

Commit 16422f4

Browse files
committed
feat(test): ability to evaluate function locator fragments as groovy scripts
Closes: #64
1 parent fb5ea75 commit 16422f4

File tree

4 files changed

+115
-1
lines changed

4 files changed

+115
-1
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@
134134
<artifactId>byte-buddy</artifactId>
135135
<version>1.14.13</version>
136136
</dependency>
137+
<dependency>
138+
<groupId>org.codehaus.groovy</groupId>
139+
<artifactId>groovy</artifactId>
140+
<version>3.0.17</version>
141+
</dependency>
137142
</dependencies>
138143

139144
<build>

src/main/java/ru/ewc/checklogic/FileStateFactory.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@
2929
import java.io.OutputStream;
3030
import java.nio.charset.StandardCharsets;
3131
import java.nio.file.Files;
32+
import java.nio.file.Paths;
3233
import java.util.ArrayList;
3334
import java.util.HashMap;
3435
import java.util.List;
3536
import java.util.Map;
3637
import lombok.SneakyThrows;
3738
import org.yaml.snakeyaml.Yaml;
39+
import ru.ewc.checklogic.testing.FunctionsLocator;
3840
import ru.ewc.decisions.api.InMemoryLocator;
3941
import ru.ewc.decisions.api.Locator;
4042
import ru.ewc.state.State;
@@ -65,9 +67,19 @@ public State initialState() {
6567
this.locators.clear();
6668
this.loadLocatorsFromApplicationConfig();
6769
this.loadInMemoryRequestLocator();
70+
this.loadFunctionsLocator();
6871
return new State(this.locators);
6972
}
7073

74+
private void loadFunctionsLocator() {
75+
this.locators.add(
76+
new FunctionsLocator(
77+
this.config.functionsLocatorName(),
78+
Paths.get(this.config.getRoot(), "functions")
79+
)
80+
);
81+
}
82+
7183
private void loadInMemoryRequestLocator() {
7284
this.locators.add(
7385
new InMemoryLocator(this.config.requestLocatorName(), new HashMap<>())

src/main/java/ru/ewc/checklogic/ServerConfiguration.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ public ServerConfiguration(final String root) {
4848
this.parameters = new HashMap<>(
4949
Map.of(
5050
"request", "request",
51-
"command", "available"
51+
"command", "available",
52+
"function", "function"
5253
)
5354
);
5455
}
@@ -88,4 +89,8 @@ public Path applicationConfig() {
8889
public String getRoot() {
8990
return this.root;
9091
}
92+
93+
public String functionsLocatorName() {
94+
return this.parameters.get("function");
95+
}
9196
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2024 Decision-Driven Development
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package ru.ewc.checklogic.testing;
25+
26+
import groovy.lang.Binding;
27+
import groovy.util.GroovyScriptEngine;
28+
import java.net.URL;
29+
import java.nio.file.Path;
30+
import java.util.HashMap;
31+
import lombok.SneakyThrows;
32+
import ru.ewc.decisions.api.ComputationContext;
33+
import ru.ewc.decisions.api.DecitaException;
34+
import ru.ewc.decisions.api.InMemoryLocator;
35+
import ru.ewc.decisions.api.Locator;
36+
37+
/**
38+
* I am a special locator that can load functions from Groovy scripts. My main responsibility is to
39+
* run the Groovy script and return the result of the script execution.
40+
*
41+
* @since 0.4.1
42+
*/
43+
public final class FunctionsLocator implements Locator {
44+
/**
45+
* The locator's name.
46+
*/
47+
private final String name;
48+
49+
/**
50+
* The in-memory locator used to store overridden values.
51+
*/
52+
private final InMemoryLocator locator;
53+
54+
/**
55+
* Groovy script engine used to run the Groovy scripts.
56+
*/
57+
private final GroovyScriptEngine engine;
58+
59+
@SneakyThrows
60+
public FunctionsLocator(final String name, final Path path) {
61+
this.name = name;
62+
this.locator = new InMemoryLocator("locator", new HashMap<>());
63+
this.engine = new GroovyScriptEngine(new URL[]{path.toUri().toURL()});
64+
}
65+
66+
@SneakyThrows
67+
@Override
68+
public String fragmentBy(
69+
final String fragment,
70+
final ComputationContext context
71+
) throws DecitaException {
72+
final String result;
73+
if (this.locator.state().containsKey(fragment)) {
74+
result = this.locator.fragmentBy(fragment, context);
75+
} else {
76+
final Binding binding = new Binding();
77+
binding.setVariable("context", context);
78+
result = this.engine.run("%s.groovy".formatted(fragment), binding).toString();
79+
}
80+
return result;
81+
}
82+
83+
@Override
84+
public void setFragmentValue(final String fragment, final String value) {
85+
this.locator.setFragmentValue(fragment, value);
86+
}
87+
88+
@Override
89+
public String locatorName() {
90+
return this.name;
91+
}
92+
}

0 commit comments

Comments
 (0)