The app I work on currently uses Java 8. We're migrating to 17 and as part of that I need to convert some code which currently references SoyTofu to use SoySauce (Google Closure Templates). Here's the relevant parts of the class I'm working in. My goal is to have the lightest possible touch on this code, but I'm getting stymied at every turn. Note that I'm also a new Java developer, so take my lack of knowledge with a grain of salt.
import com.google.template.soy.SoyFileSet;
import com.google.template.soy.tofu.SoyTofu;
public class Foobar {
...
private SoyTofu soyTemplates;
...
@Override
protected void startUp() throws Exception {
...
String templatesProperty = "templates/mail.soy templates/mail-admin.soy";
try {
String[] templates = templatesProperty.split("\\s+");
if (templates.length > 0) {
SoyFileSet.Builder builder = SoyFileSet.builder();
for (String t : templates) {
URL url = get.Url(t);
builder.add(url);
}
soyTemplates = builder.build().compileToTofu();
}
}
...
}
public Message compose(MailItem item) {
...
Map<String, Object> ijData = new HashMap<String, Object>();
ijData.put("supported", true);
ijData.put("brand", "Accounts");
String renderSubject = soyTemplates.newRenderer(item.subject).setData(item.data).setIjData(ijData).render();
message.setSubject(renderSubject);
String renderBody = soyTemplates.newRenderer(item.body).setData(item.data).setIjData(ijData).render();
message.setContent(renderBody, MediaType.TEXT_HTML);
...
}
}
And a snippet of one of the .soy files
/**
* Account request submitted subject
*/
{template accountRequestSubmittedSubject}
{@param username : string}
{$ij.brand} account request: {$username}
{/template}
The GitHub repository for GCT has a migration doc, but it's different enough that I'm not able to get it working. It seems to indicate that I can compile the templates in real time, but doesn't suggest that it's a good idea; for speed purposes. I'm willing to take that hit though since there are only two templates.
Several parts of this code are showing deprecation warnings, which I can deal with after I get the migration working. The primary issue is that the injected data method doesn't appear to be working correctly.
When I run a unit test which references this code I get an exception
Caused by: com.google.template.soy.error.SoyCompilationException: errors during Soy compilation
file:/target/test-classes/templates/mail-admin.soy:8: error: Unknown variable.
8: {$ij.brand} account request: {$requestorUsername}
~~~
I've tried creating a separate SoySauce builder, I've tried converting the SoyTofu builder to SoySauce, but I keep hitting walls. Anyone have thoughts?
I finally got it to work. Here's how I did it.
Template files need the following changes.
.
Change from this:
{template .accountRequestSubmittedSubject}
to this
{template accountRequestSubmittedSubject}
Change from this:
/**
* @param requestorUsername : string
*/
to this:
{template accountRequestSubmittedSubject}
{@param requestorUsername : string}
map
param type must be expanded.Change from this:
{@param requestFields : map}
to this:
{@param requestFields : map<string, string>} (or whatever the map being passed in consists of)
list
param type must be expanded.Change from this:
{@param groupsToJoin : list}
to this
{@param groupsToJoin : list<string>}
integer
param type has been changed to int.Change from this:
{@param totalProfilechange : integer}
to this
{@param totalProfilechange : int}
boolean
param type has been changed to bool.Change from this:
{@param cacEligible : boolean}
to this
{@param cacEligible : bool}
isFirst
template method has been removed.
One replacement approach might be to change from this:{if not isFirst($group)}, {/if}
to this
{if $groupsToJoin.indexOf($group) > 0}, {/if}
ifempty
template method has been removed.
One approach might be to replace with an explicit check against the array length:{if $groupsToLeave.length > 0}
$ij
variables have been replaced with their key namesChange from this:
{$ij.foo>}
to this
{$foo}
$ij
must now have explicit callouts for the injected variable
Add this, just inside the template block{@inject foo: string}
Java rendering changes
It's frustrating that the Closure Templates repo has deprecated code in their examples, which caused much headache. This is how I resolved it for our codebase. Hope this helps someone else out.
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.core.MediaType;
import com.google.template.soy.data.SanitizedContent;
import com.google.template.soy.jbcsrc.api.SoySauce;
import com.google.template.soy.SoyFileSet;
public class MailService {
private SoySauce soySauce;
protected void startUp() {
String template = "templates/foo.soy";
SoyFileSet.Builder builder = SoyFileSet.builder();
URL url = MailService.class.getClassLoader().getResource(template);
builder.add(url, template);
soySauce = builder.build().compileTemplates();
}
private Message compose(MailItem item) {
Message message = messageSender.create();
// Injected data (available to all templates)
Map<String, Object> ijData = new HashMap<String, Object>();
ijData.put("foo", "FOO");
SoySauce.Renderer subjectRenderer = soySauce.renderTemplate(item.subject);
String renderSubject = subjectRenderer.setData(item.data).setIj(ijData).renderText().get();
message.setSubject(renderSubject);
SoySauce.Renderer bodyRenderer = soySauce.renderTemplate(item.body);
SanitizedContent renderBody = bodyRenderer.setData(item.data).setIj(ijData).renderHtml().get();
message.setContent(renderBody.coerceToString(), MediaType.TEXT_HTML);
return message;
}
}