#86 - Add branding support to email templates - #87
Conversation
There was a problem hiding this comment.
Pull request overview
Adds shared-tenant branding support (logo, company info, brand colors) to the email notifications plugin by introducing branding loaders and Mustache partial templates, and updates/enables fast tests to validate rendering with tenant-provided branding data.
Changes:
- Introduces
EmailBrandingLoaderand associated data classes/keys to fetch and merge branding data from tenant KV. - Adds Mustache partials (
tenantStyleInfo,tenantLogoInfo,tenantCompanyInfo) and a new invoice template variant consuming them. - Refactors and re-enables fast template-rendering tests, adding a new test covering tenant branding variables.
Reviewed changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/test/resources/org/killbill/billing/plugin/notification/templates/logoInfo.json | Test logo payload used to validate tenant logo rendering. |
| src/test/resources/org/killbill/billing/plugin/notification/templates/InvoiceCreation-new-fields.mustache | New invoice template variant using branding partials. |
| src/test/resources/org/killbill/billing/plugin/notification/templates/companyInfo.json | Test company info JSON used by branding loader tests. |
| src/test/resources/org/killbill/billing/plugin/notification/templates/brandInfo.json | Test brand color JSON used by branding loader tests. |
| src/test/java/org/killbill/billing/plugin/notification/generator/TestTemplateRenderer.java | Re-enables/refactors fast tests and adds branding-specific test coverage. |
| src/main/resources/org/killbill/billing/plugin/notification/templates/tenantStyleInfo.mustache | Partial defining CSS variables from tenant brand info. |
| src/main/resources/org/killbill/billing/plugin/notification/templates/tenantLogoInfo.mustache | Partial to render tenant logo in templates. |
| src/main/resources/org/killbill/billing/plugin/notification/templates/tenantCompanyInfo.mustache | Partial to render tenant company information. |
| src/main/java/org/killbill/billing/plugin/notification/templates/MustacheTemplateEngine.java | Adds a shared Mustache compiler configured to resolve partials from classpath resources. |
| src/main/java/org/killbill/billing/plugin/notification/generator/TemplateRenderer.java | Injects company/logo/brand objects into the Mustache rendering data model. |
| src/main/java/org/killbill/billing/plugin/notification/email/branding/LogoInfo.java | New POJO for logo data. |
| src/main/java/org/killbill/billing/plugin/notification/email/branding/EmailBrandingLoader.java | New loader to retrieve and merge branding data from tenant KV. |
| src/main/java/org/killbill/billing/plugin/notification/email/branding/EmailBrandingKey.java | New enum defining tenant KV keys for branding. |
| src/main/java/org/killbill/billing/plugin/notification/email/branding/CompanyInfo.java | New POJO for company info data. |
| src/main/java/org/killbill/billing/plugin/notification/email/branding/BrandInfo.java | New POJO for brand color data. |
| .idea/misc.xml | Updates IDE metadata (project JDK and external storage config). |
Files not reviewed (1)
- .idea/misc.xml: Generated file
Suppressed comments (2)
src/main/java/org/killbill/billing/plugin/notification/email/branding/EmailBrandingLoader.java:75
- Issue #86 calls for shared tenant branding to take precedence. getEmailTemplateLogoInfo currently prefers EMAIL_TEMPLATE_LOGO_INFO over LOGO_INFO, which makes template-specific branding override shared branding.
public LogoInfo getEmailTemplateLogoInfo(final TenantContext context) {
String value = getValue(EmailBrandingKey.EMAIL_TEMPLATE_LOGO_INFO, "email template logo info", context);
if (value == null) {
value = getValue(EmailBrandingKey.LOGO_INFO, "company info", context);
}
return deserializeJson(value, LogoInfo.class, new LogoInfo(DEFAULT_PROPERTIES.get("logo")));
src/main/java/org/killbill/billing/plugin/notification/email/branding/EmailBrandingLoader.java:82
- Brand color precedence is currently template-specific first (EMAIL_TEMPLATE_BRAND_INFO overrides BRAND_INFO). Per issue #86, shared tenant branding should be used when available, with template-specific values only as fallback.
public BrandInfo getEmailTemplateBrandInfo(final TenantContext context) {
final BrandInfo invoiceTemplate = loadBrandInfo(EmailBrandingKey.EMAIL_TEMPLATE_BRAND_INFO, "email template brand info", context);
final BrandInfo global = loadBrandInfo(EmailBrandingKey.BRAND_INFO, "brand info", context);
return mergeBrandInfo(invoiceTemplate, global);
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if (values.size() > 1) { | ||
| throw new IllegalStateException(String.format( | ||
| "Unexpected number of values %d for %s and tenant %d", | ||
| values.size(), msg, tenantContext.getTenantId())); | ||
| } |
| public CompanyInfo getEmailTemplateCompanyInfo(final TenantContext context) { | ||
| String value = getValue(EmailBrandingKey.EMAIL_TEMPLATE_COMPANY_INFO, "email template company info", context); | ||
| if (value == null) { | ||
| value = getValue(EmailBrandingKey.COMPANY_INFO, "company info", context); | ||
| } | ||
| return deserializeJson(value, CompanyInfo.class, new CompanyInfo(DEFAULT_PROPERTIES.get("companyName"), DEFAULT_PROPERTIES.get("companyAddress"), DEFAULT_PROPERTIES.get("companyCityProvincePostalCode"), DEFAULT_PROPERTIES.get("companyCountry"), DEFAULT_PROPERTIES.get("companyUrl"))); | ||
| } |
There was a problem hiding this comment.
The code behavior is correct, ignoring this one.
| private String getValue(final EmailBrandingKey key, | ||
| final String description, | ||
| final TenantContext tenantContext) { | ||
| final List<String> values; | ||
| try { | ||
| values = tenantApi.getTenantValuesForKey(key.toString(), tenantContext); | ||
| return getUniqueValue(values, description, tenantContext); | ||
| } catch (final TenantApiException e) { | ||
| //TODO_86 - add a log statement | ||
|
|
||
| } | ||
| return null; | ||
| } |
| @@ -0,0 +1 @@ | |||
| {{#logo}}<img src="{{{logo}}}" style="width:100%; max-width:300px;">{{/logo}} No newline at end of file | |||
| <project version="4"> | ||
| <component name="ExternalStorageConfigurationManager" enabled="true" /> | ||
| <component name="FrameworkDetectionExcludesConfiguration"> | ||
| <file type="Osmorc" url="file://$PROJECT_DIR$" /> | ||
| </component> |
| </option> | ||
| </component> | ||
| <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK" /> | ||
| <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="11" project-jdk-type="JavaSDK" /> |
sbrossie
left a comment
There was a problem hiding this comment.
I only looked at the diff so I may be missing the big picture: Does this PR contain everything we need? Could you add some screenshots for the default branding, logo we provide?
| EMAIL_TEMPLATE_COMPANY_INFO, | ||
| EMAIL_TEMPLATE_BRAND_INFO, | ||
| EMAIL_TEMPLATE_LOGO_INFO, | ||
| EMAIL_TEMPLATE_WITH_BRANDING |
There was a problem hiding this comment.
Do we need this key - where is it used?
There was a problem hiding this comment.
Not required, was a copy paste error, I have removed it in 043fbe2.
| @@ -0,0 +1,219 @@ | |||
| <!doctype html> | |||
There was a problem hiding this comment.
Do we need to modify the other template types - seems like from a symmetry aspect, if we modify InvoiceCreation, any other type should also be modified?
There was a problem hiding this comment.
This template is created just for unit tests. See this test. I did not write unit tests for all the template types and so did not add email templates for the other email types.
Here are some screenshots. Default template (without the new branding variables):
Custom template (with new branding variables), default values for the company/brand/logo information (let me know if you think these default values should be changed, they are defined here in the code):
Custom template (with new branding variables), custom values for the company/brand/logo information:
|
sbrossie
left a comment
There was a problem hiding this comment.
Were you able to deploy on snapshot - or simply make a release - on our QA env to test if this provides the full story with Aviate - i.e. Email Configuration?



Work for #86:
0.24.xwork)