fix: enhance global key validation and update template parameters
This commit is contained in:
+5
-1
@@ -18,9 +18,13 @@ public abstract class BaseViewExtension<TTarget> implements FlashExtension {
|
|||||||
if (k.isEmpty()) {
|
if (k.isEmpty()) {
|
||||||
throw new IllegalArgumentException("global key must not be blank");
|
throw new IllegalArgumentException("global key must not be blank");
|
||||||
}
|
}
|
||||||
if (k.equals("global") || k.contains(".")) {
|
if (k.contains(".")) {
|
||||||
throw new IllegalArgumentException("global key must be a simple key (no dots), received: " + key);
|
throw new IllegalArgumentException("global key must be a simple key (no dots), received: " + key);
|
||||||
}
|
}
|
||||||
|
boolean duplicate = globals.stream().anyMatch(g -> g.key().equals(k));
|
||||||
|
if (duplicate) {
|
||||||
|
throw new IllegalArgumentException("global key '" + k + "' is already registered");
|
||||||
|
}
|
||||||
globals.add(new GlobalValue(k, Objects.requireNonNull(resolver, "global resolver must not be null")));
|
globals.add(new GlobalValue(k, Objects.requireNonNull(resolver, "global resolver must not be null")));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Binary file not shown.
+6
-7
@@ -1,11 +1,10 @@
|
|||||||
package gg.jte.generated.ondemand.pages;
|
package gg.jte.generated.ondemand.pages;
|
||||||
import dev.relism.flash.ext.view.jte.model.HomePage;
|
import dev.relism.flash.ext.view.jte.model.HomePage;
|
||||||
import java.util.Map;
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public final class JtehomeGenerated {
|
public final class JtehomeGenerated {
|
||||||
public static final String JTE_NAME = "pages/home.jte";
|
public static final String JTE_NAME = "pages/home.jte";
|
||||||
public static final int[] JTE_LINE_INFO = {0,0,1,2,2,2,2,6,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,2,3,4,4,4,4};
|
public static final int[] JTE_LINE_INFO = {0,0,1,1,1,1,5,5,5,5,6,6,6,7,7,7,8,8,8,8,8,8,1,2,3,3,3,3};
|
||||||
public static void render(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, HomePage page, String build, Map<String, Object> global) {
|
public static void render(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, HomePage page, String build, String appName) {
|
||||||
jteOutput.writeContent("\n<h1>");
|
jteOutput.writeContent("\n<h1>");
|
||||||
jteOutput.setContext("h1", null);
|
jteOutput.setContext("h1", null);
|
||||||
jteOutput.writeUserContent(page.title());
|
jteOutput.writeUserContent(page.title());
|
||||||
@@ -17,13 +16,13 @@ public final class JtehomeGenerated {
|
|||||||
jteOutput.writeUserContent(build);
|
jteOutput.writeUserContent(build);
|
||||||
jteOutput.writeContent("</small>\n<small>");
|
jteOutput.writeContent("</small>\n<small>");
|
||||||
jteOutput.setContext("small", null);
|
jteOutput.setContext("small", null);
|
||||||
jteOutput.writeUserContent((String) global.get("appName"));
|
jteOutput.writeUserContent(appName);
|
||||||
jteOutput.writeContent("</small>\n");
|
jteOutput.writeContent("</small>");
|
||||||
}
|
}
|
||||||
public static void renderMap(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, java.util.Map<String, Object> params) {
|
public static void renderMap(gg.jte.html.HtmlTemplateOutput jteOutput, gg.jte.html.HtmlInterceptor jteHtmlInterceptor, java.util.Map<String, Object> params) {
|
||||||
HomePage page = (HomePage)params.get("page");
|
HomePage page = (HomePage)params.get("page");
|
||||||
String build = (String)params.get("build");
|
String build = (String)params.get("build");
|
||||||
Map<String, Object> global = (Map<String, Object>)params.get("global");
|
String appName = (String)params.get("appName");
|
||||||
render(jteOutput, jteHtmlInterceptor, page, build, global);
|
render(jteOutput, jteHtmlInterceptor, page, build, appName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-16
@@ -22,7 +22,6 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
final class JteRuntime implements ViewRuntimeBridge<JteTarget> {
|
final class JteRuntime implements ViewRuntimeBridge<JteTarget> {
|
||||||
private static final String GLOBAL_NAMESPACE = "global";
|
|
||||||
|
|
||||||
private final JteSettings settings;
|
private final JteSettings settings;
|
||||||
private final List<GlobalValue> globals;
|
private final List<GlobalValue> globals;
|
||||||
@@ -60,30 +59,25 @@ final class JteRuntime implements ViewRuntimeBridge<JteTarget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ViewModel merge(Request req, ViewModel local) {
|
private ViewModel merge(Request req, ViewModel local) {
|
||||||
LinkedHashMap<String, Object> values = null;
|
LinkedHashMap<String, Object> values = new LinkedHashMap<>();
|
||||||
|
|
||||||
if (!globals.isEmpty()) {
|
for (GlobalValue binding : globals) {
|
||||||
LinkedHashMap<String, Object> globalMap = new LinkedHashMap<>();
|
Object resolved = binding.resolver().apply(req);
|
||||||
for (GlobalValue binding : globals) {
|
values.put(binding.key(), ViewModel.unwrapValue(resolved));
|
||||||
Object resolved = binding.resolver().apply(req);
|
|
||||||
globalMap.put(binding.key(), ViewModel.unwrapValue(resolved));
|
|
||||||
}
|
|
||||||
if (!globalMap.isEmpty()) {
|
|
||||||
values = new LinkedHashMap<>();
|
|
||||||
values.put(GLOBAL_NAMESPACE, Collections.unmodifiableMap(globalMap));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local != null) {
|
if (local != null) {
|
||||||
Map<String, Object> localMap = local.toMap();
|
Map<String, Object> localMap = local.toMap();
|
||||||
if (localMap.containsKey(GLOBAL_NAMESPACE)) {
|
for (String key : localMap.keySet()) {
|
||||||
throw new IllegalStateException("ViewModel key 'global' is reserved for framework globals");
|
if (values.containsKey(key)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"ViewModel key '" + key + "' conflicts with a registered global");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (values == null) return ViewModel.copyOf(local);
|
|
||||||
values.putAll(localMap);
|
values.putAll(localMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values == null || values.isEmpty()) return ViewModel.empty();
|
if (values.isEmpty()) return ViewModel.empty();
|
||||||
return ViewModel.owned(values);
|
return ViewModel.owned(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -38,7 +38,7 @@ class JteRuntimeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void render_rejectsReservedGlobalKeyInLocalModel() {
|
void render_rejectsConflictingLocalKey() {
|
||||||
JteRuntime runtime = new JteRuntime(
|
JteRuntime runtime = new JteRuntime(
|
||||||
JteSettings.builder().templateRoot("/templates").developmentMode(true).build(),
|
JteSettings.builder().templateRoot("/templates").developmentMode(true).build(),
|
||||||
List.of(new GlobalValue("appName", req -> "Flash"))
|
List.of(new GlobalValue("appName", req -> "Flash"))
|
||||||
@@ -48,12 +48,12 @@ class JteRuntimeTest {
|
|||||||
runtime.render(new JteHandler() {
|
runtime.render(new JteHandler() {
|
||||||
@Override
|
@Override
|
||||||
public ViewModel render(Request req) {
|
public ViewModel render(Request req) {
|
||||||
return ViewModel.of("global", "bad");
|
return ViewModel.of("appName", "override"); // collide con global
|
||||||
}
|
}
|
||||||
}, new JteTarget("pages/home.jte", ContentType.TEXT_HTML), null, new Response(200, ContentType.JSON))
|
}, new JteTarget("pages/home.jte", ContentType.TEXT_HTML), null, new Response(200, ContentType.JSON))
|
||||||
);
|
);
|
||||||
|
|
||||||
assertTrue(ex.getMessage().contains("reserved"));
|
assertTrue(ex.getMessage().contains("appName"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
@import dev.relism.flash.ext.view.jte.model.HomePage
|
@import dev.relism.flash.ext.view.jte.model.HomePage
|
||||||
@import java.util.Map
|
|
||||||
@param HomePage page
|
@param HomePage page
|
||||||
@param String build
|
@param String build
|
||||||
@param Map<String, Object> global
|
@param String appName
|
||||||
|
|
||||||
<h1>${page.title()}</h1>
|
<h1>${page.title()}</h1>
|
||||||
<p>${page.author()}</p>
|
<p>${page.author()}</p>
|
||||||
<small>${build}</small>
|
<small>${build}</small>
|
||||||
<small>${(String) global.get("appName")}</small>
|
<small>${appName}</small>
|
||||||
Reference in New Issue
Block a user