Discover custom ViewModels and validate Computed bindings in shared hosts
This commit is contained in:
+68
-4
@@ -11,7 +11,39 @@ public record BindingMetadata(
|
||||
List<Target> targets,
|
||||
List<String> diagnostics,
|
||||
List<Converter> converters,
|
||||
List<Problem> problems) {
|
||||
List<Problem> problems,
|
||||
String controller,
|
||||
String viewModel,
|
||||
List<Computed> computed) {
|
||||
public record Computed(String method, String returnType, String status, String reason) {
|
||||
public String toString() {
|
||||
return method + " : " + returnType + " — " + status + " (" + reason + ")";
|
||||
}
|
||||
}
|
||||
|
||||
public BindingMetadata(
|
||||
String model,
|
||||
String path,
|
||||
String direction,
|
||||
List<Attribute> attributes,
|
||||
List<Target> targets,
|
||||
List<String> diagnostics,
|
||||
List<Converter> converters,
|
||||
List<Problem> problems) {
|
||||
this(
|
||||
model,
|
||||
path,
|
||||
direction,
|
||||
attributes,
|
||||
targets,
|
||||
diagnostics,
|
||||
converters,
|
||||
problems,
|
||||
"",
|
||||
"",
|
||||
List.of());
|
||||
}
|
||||
|
||||
public record Converter(String name, String status, String reason) {
|
||||
public String toString() {
|
||||
return name + " — " + status + ": " + reason;
|
||||
@@ -45,13 +77,17 @@ public record BindingMetadata(
|
||||
diagnostics = List.copyOf(diagnostics);
|
||||
converters = List.copyOf(converters);
|
||||
problems = List.copyOf(problems);
|
||||
computed = List.copyOf(computed);
|
||||
if (!Set.of("view", "model", "both").contains(direction))
|
||||
throw new IllegalArgumentException("Invalid direction");
|
||||
}
|
||||
|
||||
public Map<String, String> encode() {
|
||||
var m = new TreeMap<String, String>();
|
||||
m.put("format", "2");
|
||||
m.put("format", "3");
|
||||
m.put("controller", controller);
|
||||
m.put("viewModel", viewModel);
|
||||
m.put("computed", "" + computed.size());
|
||||
m.put("converters", "" + converters.size());
|
||||
m.put("problems", "" + problems.size());
|
||||
m.put("model", model);
|
||||
@@ -93,6 +129,14 @@ public record BindingMetadata(
|
||||
m.put(p + "severity", v.severity());
|
||||
m.put(p + "message", v.message());
|
||||
}
|
||||
for (int i = 0; i < computed.size(); i++) {
|
||||
var c = computed.get(i);
|
||||
String p = "m." + i + ".";
|
||||
m.put(p + "method", c.method());
|
||||
m.put(p + "type", c.returnType());
|
||||
m.put(p + "status", c.status());
|
||||
m.put(p + "reason", c.reason());
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -107,7 +151,7 @@ public record BindingMetadata(
|
||||
}
|
||||
|
||||
public static BindingMetadata decode(Map<String, String> m) {
|
||||
if (!"2".equals(field(m, "format")))
|
||||
if (!"3".equals(field(m, "format")))
|
||||
throw new IllegalArgumentException("Unsupported binding metadata");
|
||||
var a = new ArrayList<Attribute>();
|
||||
var t = new ArrayList<Target>();
|
||||
@@ -147,7 +191,27 @@ public record BindingMetadata(
|
||||
field(m, p + "severity"),
|
||||
field(m, p + "message")));
|
||||
}
|
||||
var methods = new ArrayList<Computed>();
|
||||
for (int i = 0; i < count(m, "computed"); i++) {
|
||||
String p = "m." + i + ".";
|
||||
methods.add(
|
||||
new Computed(
|
||||
field(m, p + "method"),
|
||||
field(m, p + "type"),
|
||||
field(m, p + "status"),
|
||||
field(m, p + "reason")));
|
||||
}
|
||||
return new BindingMetadata(
|
||||
field(m, "model"), field(m, "path"), field(m, "direction"), a, t, d, c, v);
|
||||
field(m, "model"),
|
||||
field(m, "path"),
|
||||
field(m, "direction"),
|
||||
a,
|
||||
t,
|
||||
d,
|
||||
c,
|
||||
v,
|
||||
field(m, "controller"),
|
||||
field(m, "viewModel"),
|
||||
methods);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -82,8 +82,8 @@ public final class ProtocolSession implements AutoCloseable {
|
||||
&& supported.contains("interactive-v1")
|
||||
&& hello.capabilities().contains("interactive-v1")) usable.add(Operation.INTERACTIVE);
|
||||
if (hello.metadata().major() == 1
|
||||
&& supported.contains("bindings-v2")
|
||||
&& hello.capabilities().contains("bindings-v2")) usable.add(Operation.BINDINGS);
|
||||
&& supported.contains("bindings-v3")
|
||||
&& hello.capabilities().contains("bindings-v3")) usable.add(Operation.BINDINGS);
|
||||
var requiredByEither = new HashSet<>(required);
|
||||
requiredByEither.addAll(hello.required());
|
||||
if ((requiredByEither.contains("discover-v1") && !usable.contains(Operation.DISCOVER))
|
||||
@@ -92,7 +92,7 @@ public final class ProtocolSession implements AutoCloseable {
|
||||
if ((requiredByEither.contains("layout-v1") && !usable.contains(Operation.MEASURE))
|
||||
|| (requiredByEither.contains("interactive-v1") && !usable.contains(Operation.INTERACTIVE)))
|
||||
throw new IllegalArgumentException("Required capability version incompatible");
|
||||
if (requiredByEither.contains("bindings-v2") && !usable.contains(Operation.BINDINGS))
|
||||
if (requiredByEither.contains("bindings-v3") && !usable.contains(Operation.BINDINGS))
|
||||
throw new IllegalArgumentException("Required capability version incompatible");
|
||||
operations = Set.copyOf(usable);
|
||||
state = State.READY;
|
||||
|
||||
+12
-7
@@ -21,7 +21,12 @@ class BindingMetadataTest {
|
||||
"input", "text", "java.lang.String", "COMPATIBLE", "Types match")),
|
||||
List.of(),
|
||||
List.of(new BindingMetadata.Converter("app.C", "UNVERIFIED", "No metadata")),
|
||||
List.of(new BindingMetadata.Problem("input", "text", "ERROR", "Missing path")));
|
||||
List.of(new BindingMetadata.Problem("input", "text", "ERROR", "Missing path")),
|
||||
"app.Controller",
|
||||
"app.ViewModel",
|
||||
List.of(
|
||||
new BindingMetadata.Computed(
|
||||
"#caption", "java.lang.String", "COMPATIBLE", "Declared type")));
|
||||
assertEquals(
|
||||
m, BindingMetadata.decode(PreviewMessages.decode(PreviewMessages.encode(m.encode()))));
|
||||
var id = new Identity("s", "r", "c", "b", Map.of("file", 1L), 0, 0);
|
||||
@@ -43,7 +48,7 @@ class BindingMetadataTest {
|
||||
@Test
|
||||
void negotiationRequiresMetadataVersion() throws Exception {
|
||||
String token = ProtocolSession.newToken();
|
||||
var session = new ProtocolSession("s", 1, token, Set.of("bindings-v2"), Set.of("bindings-v2"));
|
||||
var session = new ProtocolSession("s", 1, token, Set.of("bindings-v3"), Set.of("bindings-v3"));
|
||||
assertTrue(
|
||||
session
|
||||
.authenticate(
|
||||
@@ -55,11 +60,11 @@ class BindingMetadataTest {
|
||||
new Version(1, 0),
|
||||
new Version(1, 0),
|
||||
new Version(1, 0),
|
||||
Set.of("bindings-v2"),
|
||||
Set.of("bindings-v3"),
|
||||
Set.of()))
|
||||
.contains(Operation.BINDINGS));
|
||||
var oldWorker =
|
||||
new ProtocolSession("s", 1, token, Set.of("bindings-v2"), Set.of("bindings-v2"));
|
||||
new ProtocolSession("s", 1, token, Set.of("bindings-v3"), Set.of("bindings-v3"));
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
@@ -72,10 +77,10 @@ class BindingMetadataTest {
|
||||
new Version(1, 0),
|
||||
new Version(1, 0),
|
||||
new Version(1, 0),
|
||||
Set.of("bindings-v1"),
|
||||
Set.of("bindings-v2"),
|
||||
Set.of())));
|
||||
var incompatible =
|
||||
new ProtocolSession("s", 1, token, Set.of("bindings-v2"), Set.of("bindings-v2"));
|
||||
new ProtocolSession("s", 1, token, Set.of("bindings-v3"), Set.of("bindings-v3"));
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
@@ -88,7 +93,7 @@ class BindingMetadataTest {
|
||||
new Version(1, 0),
|
||||
new Version(2, 0),
|
||||
new Version(1, 0),
|
||||
Set.of("bindings-v2"),
|
||||
Set.of("bindings-v3"),
|
||||
Set.of())));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user