This commit is contained in:
Guillaume Dugas
2026-02-19 09:58:13 +01:00
commit 7ad05e5b14
16 changed files with 414 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
package fr.cnd.compositor.blocks.models;
import lombok.*;
import java.util.List;
import java.util.Map;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BlockRenderDefinition {
String name;
Map<String, Object> inputs;
Map<String, List<BlockRenderDefinition>> slots;
}

View File

@@ -0,0 +1,15 @@
package fr.cnd.compositor.blocks.models;
import lombok.*;
import java.util.List;
import java.util.Map;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BlockRenderRequest {
Map<String, List<BlockRenderDefinition>> definitions;
}

View File

@@ -0,0 +1,15 @@
package fr.cnd.compositor.blocks.models;
import lombok.*;
import java.util.List;
import java.util.Map;
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BlockRenderResult {
Map<String, List<String>> result;
}

View File

@@ -0,0 +1,28 @@
package fr.cnd.compositor.blocks.pebble;
import fr.cnd.compositor.blocks.models.BlockRenderDefinition;
import fr.cnd.compositor.blocks.specs.BlockTemplate;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public abstract class AbstractPebbleBlockTemplate implements BlockTemplate {
@Inject
PebbleBlockEngine engine;
public void setEngine(PebbleBlockEngine engine) {
this.engine = engine;
}
public abstract Uni<String> getTemplate();
@Override
public Uni<String> render(BlockRenderDefinition definition) {
final Map<String, Object> context = new HashMap<>(Optional.ofNullable(definition.getInputs()).orElse(new HashMap<>()));
return getTemplate()
.chain(template -> engine.render(template, context));
}
}

View File

@@ -0,0 +1,19 @@
package fr.cnd.compositor.blocks.pebble;
import io.pebbletemplates.pebble.extension.AbstractExtension;
import io.pebbletemplates.pebble.extension.Function;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.Map;
@ApplicationScoped
public class PebbleBlocksExtension extends AbstractExtension {
@Inject
PebbleSlotFunction pebbleSlotFunction;
public Map<String, Function> getFunctions() {
return Map.of("slot", pebbleSlotFunction);
}
}

View File

@@ -0,0 +1,33 @@
package fr.cnd.compositor.blocks.pebble;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.pebbletemplates.pebble.extension.Function;
import io.pebbletemplates.pebble.extension.escaper.SafeString;
import io.pebbletemplates.pebble.template.EvaluationContext;
import io.pebbletemplates.pebble.template.PebbleTemplate;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.List;
import java.util.Map;
@ApplicationScoped
public class PebbleSlotFunction implements Function {
@Inject
ObjectMapper objectMapper;
@Override
public SafeString execute(Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
final Object slotsVar = context.getVariable("__slots__");
final Map<String, String> slots = objectMapper.convertValue(slotsVar, new TypeReference<>() {});
final String slotName = (String) args.get("name");
return slots != null && slots.containsKey(slotName) ? new SafeString(slots.get(slotName)) : new SafeString("");
}
@Override
public List<String> getArgumentNames() {
return List.of("name");
}
}

View File

@@ -0,0 +1,10 @@
package fr.cnd.compositor.blocks.specs;
import io.smallrye.mutiny.Uni;
import java.util.Map;
public interface BlockConfiguration {
Uni<String> getBlockTemplateName();
Uni<Map<String, Object>> mapInputs(Map<String, Object> inputs);
}

View File

@@ -0,0 +1,19 @@
package fr.cnd.compositor.blocks.specs;
import io.smallrye.mutiny.Uni;
import java.util.Map;
/**
* Maps user-provided inputs to the final template context.
*/
public interface ContextMapping {
/**
* Transforms the given user inputs into the context that will be passed to the template engine.
*
* @param inputs the raw user-provided input entries
* @return a {@link Uni} emitting the resulting template context
*/
Uni<Map<String, Object>> map(Map<String, Object> inputs);
}

View File

@@ -0,0 +1,8 @@
package fr.cnd.compositor.blocks.specs;
import io.smallrye.mutiny.Uni;
public interface ContextMappingResolver {
Uni<ContextMapping> resolveContextMapping(String contextMappingIdentifier);
}

View File

@@ -0,0 +1,24 @@
package fr.cnd.compositor.blocks.templates;
import fr.cnd.compositor.blocks.pebble.AbstractPebbleBlockTemplate;
import io.smallrye.common.annotation.Identifier;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
@Identifier("html")
@ApplicationScoped
public class HtmlBlockTemplate extends AbstractPebbleBlockTemplate {
@Override
public Uni<String> getTemplate() {
return Uni.createFrom().item("""
<html>
<head>
<title>{{title}}</title>
</head>
<body>
{{content}}
</body>
</html>
""");
}
}

View File

@@ -0,0 +1,35 @@
package fr.cnd.compositor.blocks.templates;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import fr.cnd.compositor.blocks.specs.BlockConfiguration;
import io.smallrye.common.annotation.Identifier;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.HashMap;
import java.util.Map;
@Identifier("product")
@ApplicationScoped
public class HtmlProductConfiguration implements BlockConfiguration {
@Inject
ObjectMapper objectMapper;
@Override
public Uni<String> getBlockTemplateName() {
return Uni.createFrom().item("html");
}
@Override
public Uni<Map<String, Object>> mapInputs(Map<String, Object> inputs) {
Map<String, String> product = objectMapper.convertValue(inputs.getOrDefault("product", new HashMap<>()), new TypeReference<>() {});
return Uni.createFrom().item(Map.of(
"title", product.getOrDefault("title", ""),
"content", product.getOrDefault("name", "")
));
}
}