Add index-support, type hints and improve documentation #4

Merged
stne3960 merged 3 commits from feature/allow_index into main 2025-06-16 12:41:04 +02:00
Showing only changes of commit ddc0673e95 - Show all commits

View File

@ -6,6 +6,8 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import dsv.su.apimposter.model.Condition; import dsv.su.apimposter.model.Condition;
import dsv.su.apimposter.model.ConditionalResponse; import dsv.su.apimposter.model.ConditionalResponse;
@ -28,6 +30,7 @@ public class MockService {
private final MockFileLoader loader; private final MockFileLoader loader;
private final AntPathMatcher pathMatcher = new AntPathMatcher(); private final AntPathMatcher pathMatcher = new AntPathMatcher();
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{([\\w\\.]+)(?::(int|float|bool))?}");
/** /**
* Initializes the mock service with a reference to the file loader, * Initializes the mock service with a reference to the file loader,
@ -113,7 +116,12 @@ public class MockService {
/** /**
* Recursively replaces placeholders in the mock response body with actual values * Recursively replaces placeholders in the mock response body with actual values
* from path variables and global placeholders. * Supports simple and typed placeholders:
* - {@code {id}} - from path variables
* - {@code {id:int}} - from path variables, converted to int
* - {@code {globals.env}} - from globals
* - {@code {globals.port:int}} - from globals, converted to int
* If type conversion fails (e.g., {@code {port:int}} but value is "abc"), the raw string is used.
* @param body The response body template. * @param body The response body template.
* @param pathVariables Extracted values from the request URL path. * @param pathVariables Extracted values from the request URL path.
* @return Response body with all placeholders replaced. * @return Response body with all placeholders replaced.
@ -134,20 +142,74 @@ public class MockService {
.toList(); .toList();
} else if (body instanceof String str) { } else if (body instanceof String str) {
for (var entry : pathVariables.entrySet()) { Matcher matcher = PLACEHOLDER_PATTERN.matcher(str);
str = str.replace("{" + entry.getKey() + "}", entry.getValue()); // If the string is a single placeholder (e.g. "{id:int}" or "{id}"), resolve and return its value
if (matcher.matches()) {
String key = matcher.group(1);
String type = matcher.group(2);
String raw = resolveValue(key, pathVariables, globals);
return convertWithType(raw, type);
} }
for (var entry : globals.entrySet()) { // Otherwise: perform inline replacement for all placeholders in the string
str = str.replace("{globals." + entry.getKey() + "}", entry.getValue().toString()); StringBuilder sb = new StringBuilder();
while (matcher.find()) {
String key = matcher.group(1);
String type = matcher.group(2);
String raw = resolveValue(key, pathVariables, globals);
Object replacement = convertWithType(raw, type);
matcher.appendReplacement(sb, Matcher.quoteReplacement(String.valueOf(replacement)));
} }
matcher.appendTail(sb);
return str; return sb.toString();
} }
return body; return body;
} }
/**
* Attempts to convert a raw string into a typed value if a type hint is provided.
* Supported types:
* - {@code int} - Integer.parseInt
* - {@code float} - Double.parseDouble
* - {@code bool} - true if "true", "1", "yes", "on" (case-insensitive)
* @param raw the string value to convert
* @param type the optional type hint
* @return the converted value, or the raw string if conversion fails or type is unknown
*/
private Object convertWithType(String raw, String type) {
if (type == null) return raw;
try {
return switch (type) {
case "int" -> Integer.parseInt(raw);
case "float" -> Double.parseDouble(raw);
case "bool" -> Boolean.parseBoolean(raw.toLowerCase());
default -> raw;
};
} catch (Exception e) {
return raw;
}
}
/**
* Resolves a placeholder value from path variables or globals.
* - If the key starts with "globals.", the value is fetched from globals.
* - Otherwise, it's fetched from the pathVariables map.
* @param key the raw key name (e.g., "id", "globals.env")
* @param pathVariables path values extracted from the URI
* @param globals map of global variables
* @return the resolved string value, or empty string if not found
*/
private String resolveValue(String key, Map<String, String> pathVariables, Map<String, Object> globals) {
if (key.startsWith("globals.")) {
String globalKey = key.substring("globals.".length());
Object globalValue = globals.get(globalKey);
return globalValue != null ? String.valueOf(globalValue) : "";
} else {
return pathVariables.getOrDefault(key, "");
}
}
/** /**
* Serializes a Java object into a JSON string. * Serializes a Java object into a JSON string.
* If serialization fails, returns an error wrapper instead. * If serialization fails, returns an error wrapper instead.