I'd like to be able to configure different json templates based on e.g. a clientId with a default template in case a template for the clientId is not defined.
Context context = new Context();
Map<String, Object> templateResolutionAttribute = new HashMap<>();
templateResolutionAttribute.put("clientId", 12345);//Trying to somehow pass a variable to the template selection process...
TemplateSpec templateSpec = new TemplateSpec(
"json/data.json",
templateResolutionAttribute
);
String output = springTemplateEngine.process(templateSpec, context);
The Spring Config class I'm using looks like this:
#Configuration
public class ThymeLeafConfig {
#Bean
public SpringResourceTemplateResolver jsonMessageTemplateResolver() {
SpringResourceTemplateResolver theResourceTemplateResolver =
new SpringResourceTemplateResolver();
theResourceTemplateResolver.setPrefix("classpath:/templates/");
theResourceTemplateResolver.setResolvablePatterns(
Collections.singleton("json/*"));
theResourceTemplateResolver.setSuffix(".json");
theResourceTemplateResolver.setCharacterEncoding("UTF-8");
theResourceTemplateResolver.setCacheable(false);
theResourceTemplateResolver.setOrder(1);
return theResourceTemplateResolver;
}
The json templates could be in different directories or alternatively have different file names (whatever works best/easiest):
json/
data.json
12345/
data.json
or
json/
data.json
data.12345.json
What is the best or easiest way to achieve returning a template that "matches" the clientId and if no match for the clientId is found return a default template?
Note: For new clients, ideally, I'd like to be able to just add new json templates without making any code changes...
Related
I'm having trouble working out what I'm doing wrong here -
#Test
void should_fill_a_string_template() {
String template = "Hello ${name}";
StringTemplateResolver resolver = new StringTemplateResolver();
Context context = new Context();
context.setVariable("name","Test");
TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(resolver);
String result = templateEngine.process(template, context);
Assertions.assertEquals("Hello Test",result);
}
I just want to use the Thymeleaf template processor in a plain old java project without other dependencies. I need to pass in a String (not a file to be resolved) and have the template filled with the values provided.
I see the default behavior here indicates that the StringTemplateResolver should just resolve the string passed in as the template itself and not a reference to the template.
What am I missing here?
I had the following problem: I have a service where I want to dynamically render templates using qute. Whose names I don't currently know (because they are passed via the endpoint).
Unfortunately Quarkus itself doesn't give the possibility to say "Template t = new Template()".... You always have to define them via inject at the beginning of a class. After a long time of searching and thinking about it, I have the following solution:
The solution is to inject the Quarkus Template Engine instead of a Template. The Engine could render a template directly.... Then we only have to read our template file as a String (Java 11 can read Files.readString(path, encoding)) and render it with our data map.
#Path("/api")
public class YourResource {
public static final String TEMPLATE_DIR = "/templates/";
#Inject
Engine engine;
#POST
public String loadTemplateDynamically(String locale, String templateName, Map<String, String> data) {
File currTemplate = new File(YourResource.class.getResource("/").getPath() + TEMPLATE_DIR + locale + "/" + templateName + ".html"); // this generates a String like <yourResources Folder> + /templates/<locale>/<templateName>.html
try {
Template t = engine.parse(Files.readString(currTemplate.getAbsoluteFile().toPath(), StandardCharsets.UTF_8));
//this render your data to the template... you also could specify it
return t.data(data.render());
} catch (IOException e) {
e.printStackTrace();
}
return "template not exists";
}
}
I am trying to add support for emails with Thymeleaf templates in my Spring Boot app. It works fine as long as I am using only templates stored as .html files. What I would like to do is add support for "overriding" these files with user configured templates. So if template exists in DB, use it. Otherwise, try to use the one from file.
My config looks as follows:
#Configuration
public class ThymeleafConfig {
#Bean
public SpringTemplateEngine springTemplateEngine(DatabaseTemplateResolver databaseTemplateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
databaseTemplateResolver.setTemplateMode(TemplateMode.HTML);
templateEngine.addTemplateResolver(databaseTemplateResolver);
return templateEngine;
}
#Bean
public SpringResourceTemplateResolver htmlTemplateResolver() {
SpringResourceTemplateResolver emailTemplateResolver = new SpringResourceTemplateResolver();
emailTemplateResolver.setPrefix("/templates/");
emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode(TemplateMode.HTML);
emailTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
return emailTemplateResolver;
}
}
And database resolver:
#RequiredArgsConstructor
#Component
public class DatabaseTemplateResolver extends StringTemplateResolver {
#Qualifier("htmlTemplateResolver")
private final SpringResourceTemplateResolver htmlTemplateResolver;
#Override
public ITemplateResource computeTemplateResource(IEngineConfiguration configuration, String ownerTemplate, String templateName, Map<String, Object> templateResolutionAttributes) {
model.runInTransaction(tx -> {
Optional<Template> template = // load template from DB;
if (template.isPresent()) {
return super.computeTemplateResource(configuration, ownerTemplate, template.get().getContent(), templateResolutionAttributes);
} else {
return htmlTemplateResolver.resolveTemplate(configuration, ownerTemplate, templateName, templateResolutionAttributes);
}
}, TransactionDescriptor.asSystemUser());
return null;
}
}
The template is found in DB here and gets returned but I get the following error:
[THYMELEAF][task-2] Exception processing template "email-notification": Error resolving template [email-notification], template might not exist or might not be accessible by any of the configured Template Resolvers
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [email-notification], template might not exist or might not be accessible by any of the configured Template Resolvers
at org.thymeleaf.engine.TemplateManager.resolveTemplate(TemplateManager.java:869)
at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:607)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1059)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1048)
Does anyone know what am I doing wrong?
I hope you are fine, Did you see this answer?
How to read a Thymeleaf template from DB
I think it's because of the prefix or suffix that you set.
I'm trying to use M2Doc programmatically, I managed to generate my .docx file without getting errors in the validation part but I'm getting the following Error in the generated document:
{m:self.Name} Couldn't find the 'aqlFeatureAccess(org.eclipse.emf.common.util.URI.Hierarchical,java.lang.String)' service
The "self.Name" part is what I wrote in my template.
I think I'm lacking some kind of reference to a service but I don't know how to fix it.
The self variable is a reference to a model based on a meta-model I created. But I'm not sure I imported it correctly in my code.
I based my code on the code I found on the M2Doc website + some code I found on their GitHub, especially concerning how to add a service in the queryEnvironment.
I searched in the source code of acceleo and M2Doc to see which services they add but it seems that they already import all the services I'm using.
As I said, the validation part is going well and doesn't generate a validation file.
public static void parseDocument(String templateName) throws Exception{
final URI templateURI = URI.createFileURI("Template/"+templateName+"."+M2DocUtils.DOCX_EXTENSION_FILE);
final IQueryEnvironment queryEnvironment =
org.eclipse.acceleo.query.runtime.Query.newEnvironmentWithDefaultServices(null);
final Map<String, String> options = new HashMap<>(); // can be empty
M2DocUtils.prepareEnvironmentServices(queryEnvironment, templateURI, options); // delegate to IServicesConfigurator
prepareEnvironmentServicesCustom(queryEnvironment, options);
final IClassProvider classProvider = new ClassProvider(ClassLoader.getSystemClassLoader()); // use M2DocPlugin.getClassProvider() when running inside Eclipse
try (DocumentTemplate template = M2DocUtils.parse(templateURI, queryEnvironment, classProvider)) {
ValidationMessageLevel validationLevel = validateDocument(template, queryEnvironment, templateName);
if(validationLevel == ValidationMessageLevel.OK){
generateDocument(template, queryEnvironment, templateName, "Model/ComplexKaosModel.kaos");
}
}
}
public static void prepareEnvironmentServicesCustom(IQueryEnvironment queryEnvironment, Map<String, String> options){
Set<IService> services = ServiceUtils.getServices(queryEnvironment, FilterService.class);
ServiceUtils.registerServices(queryEnvironment, services);
M2DocUtils.getConfigurators().forEach((configurator) -> {
ServiceUtils.registerServices(queryEnvironment, configurator.getServices(queryEnvironment, options));
});
}
public static void generateDocument(DocumentTemplate template, IQueryEnvironment queryEnvironment,
String templateName, String modelPath)throws Exception{
final Map<String, Object> variable = new HashMap<>();
variable.put("self", URI.createFileURI(modelPath));
final Monitor monitor = new BasicMonitor.Printing(System.out);
final URI outputURI = URI.createFileURI("Generated/"+templateName+".generated."+M2DocUtils.DOCX_EXTENSION_FILE);
M2DocUtils.generate(template, queryEnvironment, variable, outputURI, monitor);
}
The variable "self" contains an URI:
variable.put("self", URI.createFileURI(modelPath));
You have to load your model and set the value of self to an element from your model using something like:
final ResourceSet rs = new ResourceSetImpl();
final Resource r = rs.getResource(uri, true);
final EObject value = r.getContents()...;
variable.put("self", value);
You can get more details on resource loading in the EMF documentation.
The paging function, and a local filtering is perfect, but i need the REMOTE filter, and i wanna sending the filters parameter to the request method.
Thx!
I have this code:
String path = GWT.getHostPageBaseURL() + (Examples.isExplorer() ? "" : "../../" ) + "backend/index.php?action=getLines";
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, path);
HttpProxy<String> proxy = new HttpProxy<String>(builder);
JsonPagingLoadResultReader<PagingLoadResult<ModelData>> reader = new JsonPagingLoadResultReader<PagingLoadResult<ModelData>>(type);
final PagingLoader<PagingLoadResult<ModelData>> loader = new BasePagingLoader<PagingLoadResult<ModelData>>(proxy,
reader);
[...]
NumericFilter sorszamFilter = new NumericFilter("Sorszam");
StringFilter nevFilter = new StringFilter("Nev");
DateFilter datumFilter = new DateFilter("Datum");
NumericFilter szamFilter = new NumericFilter("Szam");
GridFilters filters = new GridFilters();
filters.setLocal(true);
filters.addFilter(sorszamFilter);
filters.addFilter(nevFilter);
filters.addFilter(datumFilter);
filters.addFilter(szamFilter);
//example
sorszamFilter.addListener(Events.Update, new Listener<FilterEvent>() {
#Override
public void handleEvent(FilterEvent be) {
???
}
});
[...]
final PagingToolBar toolBar = new PagingToolBar(10);
toolBar.bind(loader);
loader.load(0, 10);
It looks like the BasePagingLoader can get be customized using a loadConfig object. The loadConfig should be an Object of a ModelData type and more specifically a PagingLoadConfig.
Create a new loadConfig using the
final ModelData loadConfig = (ModelData) ((BasePagingLoader).loader).newLoadConfig();
method.
Then force the loader to use this loadConfig:
((BasePagingLoader).loader).useLoadConfig(loadConfig);
loadConfig should be a mutable instance of a ModelData. That is why you can add new properties to it using the
loadConfig.set("selectedFilter", "what_ever_you_like_here")
loadConfig.set("direction", "ASC");
This should be done in place of the question marks you put and should force the HttpProxy to add whatever you set to the loadConfig properties. (see the HttpProxy#generateUrl method for reference on how the request is build with an aid of a loadConfig). Then you'll have to process the request correspondingly on server-side.
I'm assuming you use GXT 2.2.x, and honestly I haven't compiled it, hope it works fine.