fix(health): email.readiness says what is missing and where to set it #29

Merged
KofTwentyTwo merged 1 commits from feature/readiness-says-where into develop 2026-10-16 16:51:33 +00:00
3 changed files with 36 additions and 2 deletions

No files matched your search

+1
View File
@@ -17,6 +17,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
- `EmailTablesGuardrailTest`: every table is used by runtime code, seeded or listed as starting empty with a reason, and has help.
### Fixed
- The `email.readiness` health message now names each missing item in plain words and where to set it (for example "Brand postal address is not set (Email > Brand > open the default brand > Postal address)") and says transactional email is unaffected by marketing-only gaps. The `failedChecks` detail is unchanged; a new `howToFix` detail lists the same text.
- Derive the qbit metadata version from the Maven artifact version so released artifacts report their release version.
- `email_template.ownerQbit` was stored but never read, and the built-in template did not set it. The built-in now names `qbit-email`, the field is read-only with help, and messages sent from a template name its owner qbit as `senderQbit` unless the request names one.
@@ -27,6 +27,7 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.kingsrook.qbits.email.EmailRuntime;
import com.kingsrook.qbits.email.api.EmailApi;
import com.kingsrook.qbits.email.api.EmailApiCategory;
import com.kingsrook.qbits.email.api.EmailApiReadinessCheck;
@@ -134,8 +135,38 @@ public class EmailHealthChecks implements HealthCheckSupplier
ready &= readiness.ready();
failed.addAll(readiness.failedChecks());
}
return ((ready ? HealthCheckOutcome.up("Email configuration ready") : HealthCheckOutcome.degraded("Email configuration incomplete"))
.withDetails(new HealthDetails().withValue("failedChecks", failed.stream().map(Enum::name).collect(Collectors.joining(",")))));
if(ready)
{
return (HealthCheckOutcome.up("Email configuration ready").withDetails(new HealthDetails().withValue("failedChecks", "")));
}
var config = EmailRuntime.config(input.getQInstance());
String app = config == null || config.getAppLabel() == null ? "Email" : config.getAppLabel();
List<String> fixes = failed.stream().map(check -> fix(check, app)).toList();
String message = "Email configuration incomplete: " + String.join(" ", fixes);
String details = String.join("\n", fixes);
return (HealthCheckOutcome.degraded(message.length() > 500 ? message.substring(0, 500) : message)
.withDetails(new HealthDetails().withValue("failedChecks", failed.stream().map(Enum::name).collect(Collectors.joining(",")))
.withValue("howToFix", details.length() > 4000 ? details.substring(0, 4000) : details)));
}
/*******************************************************************************
** Plain-words statement of one missing prerequisite and where to set it.
*******************************************************************************/
private static String fix(EmailApiReadinessCheck check, String app)
{
return (switch(check)
{
case NOT_INSTALLED -> "The Email qbit is not installed.";
case PROVIDER_MISSING -> "No enabled default email provider (" + app + " > Provider: add one and tick Enabled and Is Default).";
case PROVIDER_INVALID -> "The default email provider settings are invalid (" + app + " > Provider: open the default provider and correct it).";
case PHI_PROVIDER -> "PHI email needs a PHI-enabled provider with open and click tracking off (" + app + " > Provider).";
case PUBLIC_BASE_URL -> "Public base URL is not set (the publicBaseUrl setting in the Email qbit config). Needed for marketing unsubscribe links; transactional email is unaffected.";
case UNSUBSCRIBE_KEY -> "The QQQ_EMAIL_UNSUBSCRIBE_KEY secret is not set (deployment environment variable). Needed for marketing unsubscribe links; transactional email is unaffected.";
case BRAND_POSTAL_ADDRESS -> "Brand postal address is not set (" + app + " > Brand > open the default brand > Postal address). Needed for marketing email footers; transactional email is unaffected.";
case BODY_KEY -> "The QQQ_EMAIL_BODY_KEY secret is not set (deployment environment variable). Needed for PHI email.";
});
}
@@ -165,6 +165,8 @@ class EmailHealthChecksTest
assertThat(outcome.getStatus()).isEqualTo(HealthStatus.DEGRADED);
assertThat(outcome.getDetails().getValues().get("failedChecks").toString().split(","))
.containsExactly("PUBLIC_BASE_URL", "BRAND_POSTAL_ADDRESS");
assertThat(outcome.getMessage()).contains("Brand postal address is not set").contains("Brand > open the default brand > Postal address").contains("transactional email is unaffected").hasSizeLessThanOrEqualTo(500);
assertThat(outcome.getDetails().getValues().get("howToFix").toString()).contains("Public base URL is not set").contains("Brand postal address is not set");
configureMarketing();
assertThat(run(readinessCheck()).getStatus()).isEqualTo(HealthStatus.UP);
update("enabled", false);