Repository navigation
Author-nudge emails: investigate IMS legacy behavior + design CAP replacement #545
Description
Activity
- added a commit that references this issue
on Jun 22, 2026 From Riley about legacy:
It would only come from the Production system (if I understand the architecture correctly). Dmytro mentioned a Destination that makes the connection. I do see nag messages from as recently as June 21 (I get cc-ed on some of these by IMS, depending on severity).
So we need to research further
Update — source-code verification (2026-06-23)
Followed up on the BTP-destination-hookup hypothesis. The source code does not support it. Walking the IMS Java code end-to-end:
The 3 destinations in the IMS subaccount are HTTP API destinations, not mail
Destination Consumer Purpose SCI/SCI_prodSciClientImpl.java:17via@Value("${sci.destination-name}")→application.yaml:152SAP Cloud Identity user lookup NGDSNGDSSenderServiceImpl.java:70+NGDSTokenManagerImpl.java:39via@Value("${ngds.destination-name}")→application.yaml:238Next Gen Developer Services accomplishment messaging Both consumers use SAP Cloud SDK's
DestinationAccessor+HttpClientAccessor(seeDestinationServiceImpl.java) — that class only fetchesHttpDestinationand runsGET/DELETE. It cannot service an SMTP transport. Spring Boot mail usesJavaMailSenderwired throughMailSenderAutoConfiguration, not through the destination service.The
email.destination-name: mail/Sessionconfig value is dead configapplication/src/main/resources/application.yaml:272:email: destination-name: mail/Session default-from: developers@sap.com
Full-tree grep:
grep -rn '${email.destination-name}\|email.destination-name\|"mail/Session"\|mail/Session' application/src/main/ → application/src/main/resources/application.yaml:273: destination-name: mail/SessionZero consumers. No
@Value("${email.destination-name}")exists anywhere in the Java source. The key name was carried over from old NEO-era config —mail/Sessionis the JNDI name of a Tomcat container-managedjavax.mail.Session(a NEO Java buildpack pattern), not a BTP destination. On Cloud Foundry that JNDI name resolves to nothing.JavaMailSenderhas no production bean — only a test mockThe only
@Bean JavaMailSenderin the entire IMS repo isapplication/src/test/java/com/sap/developers/ims/configuration/MailConfiguration.java, whichMockito.mock(...)s it. Production has:- No
MailConfig.java/ no@Bean JavaMailSenderanywhere undersrc/main/ - No
spring.mail.*config inapplication.yaml(or any sibling profile file) manifest-prod.ymlbinds onlyims-hana-prod-container— no mail service, no destination-service binding
That leaves Spring Boot's
MailSenderAutoConfiguration, which only fires whenspring.mail.host(or one of its siblings) is set. A grep forspring.mailacrossapplication/,web/, andapprouter/returns nothing.MailTestControlleradmits it's a debug probeMailTestController.javais annotated withTODO: Remove this controller once application debugging is completed.— it was added precisely to test whether mail wiring worked. Hitting/test/mailonimsprodwould throwNoSuchBeanDefinitionException(or fail at startup) because there is no realJavaMailSenderbean to inject.Conclusion
IMS author-nudge email has been silently broken since the NEO→Cloud Foundry migration. The
tutorials-mailMTA resource removed on 2026-06-22 was never going to start working on the new platform either — the subaccount has no mail service offering, and the destination service can't carry SMTP credentials.Definitive runtime check (pending)
A migration is running on
imsprodright now. Once it completes and we cancf targetit again, this command will seal the picture:cf env imsprod | grep -iE "mail|smtp|spring_mail"
If that returns nothing, the cron has been throwing
EmailNotSentException(caught + swallowed at the cron boundary, never re-thrown — see the original ticket body) on every fire since CF cutover.Next steps
Holding off on the design doc until the
cf env imsprodcheck confirms the static-code reading. After that, draftdocs/superpowers/specs/2026-06-23-author-nudge-emails-design.mdcovering options A–D from the ticket.- No
Correction — runtime evidence reverses my prior reading (2026-06-23)
Just did
cf env imsprodafter the migration freed the target. My previous comment was wrong — mail IS wired up in production, just not the way the IMS Java source code suggested.What's actually wired (and how I missed it)
The IMS team set Spring Boot mail config directly on the app as User-Provided env vars (via
cf set-env):SPRING_MAIL_HOST: smtpauth.mail.net.sap SPRING_MAIL_PORT: 587 SPRING_MAIL_USERNAME: developer-rims-notifications SPRING_MAIL_PASSWORD: <REDACTED> SPRING_MAIL_PROPERTIES_MAIL_SMTP_AUTH: true SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_ENABLE: true SPRING_MAIL_PROPERTIES_MAIL_TRANSPORT_PROTOCOL: smtp SPRING_MAIL_PROPERTIES_MAIL_SMTP_CONNECTIONTIMEOUT: 5000 SPRING_MAIL_PROPERTIES_MAIL_SMTP_TIMEOUT: 5000 SPRING_MAIL_PROPERTIES_MAIL_SMTP_WRITETIMEOUT: 5000This triggers Spring Boot's
MailSenderAutoConfiguration(via relaxed binding from upper-snake-case env vars onto thespring.mail.*config keys), which materializes a realJavaMailSenderbean — no@Beandefinition needed in source, no service binding needed, no BTP destination needed.Why I missed it in the static read: these values aren't in
application.yaml, aren't in anymanifest-*.yml, and don't show up as a service binding. They're operator-set env on the running app, invisible to the repo. The screenshot of the 3 BTP destinations (NGDS,SCI,SCI_prod) was a red herring — mail in this stack is plain SMTP overcf set-env, not destination-mediated.What this means for the original ticket
The first hypothesis in the issue body — "
mail/SessionJNDI lookup fails, cron silently errors" — is also wrong. There IS a working mail transport. So the relevant question for #545 shifts to: does the cron actually fire, and if so does it send? Possibilities:- Working as designed:
isNotificationSendingAllowed='true'in PROD DB, cron fires daily at 09:00 UTC, emails are flowing to authors, and we've just been unaware of it. - Flag-disabled:
isNotificationSendingAllowed='false'in PROD DB, cron exits cleanly, no emails sent regardless of SMTP working. - Half-broken: cron fires, SMTP send succeeds, but recipient list is stale /
emailListForOutdatedempty / TutorialContributors table empty.
cf logs imsprod --recentonly covers the live tail (~minutes), not back to today's 09:00 UTC cron fire. To distinguish (1)/(2)/(3) I need either:- A direct query against
IMSDBUSER.ims_configfor theisNotificationSendingAllowedrow and theemailListForOutdatedrow (creds are in the env — DB_USERNAME / DB_PASSWORD point at the legacy IMSDBUSER schema) - Live
cf logs imsprodtail tomorrow at 09:00 UTC to catch the next fire - Or pulling logs from
imsprod-logs(Application Logging service) if retention covers today
Updated implications for the design
Option C/D from the original ticket (skip email entirely / GitHub-issue-based nudges) are still viable for the new CAP tutorials-srv, but the original premise — "IMS email has been silently broken anyway" — does NOT hold. If we want behavioral continuity at PROD cutover, we either:
- Carry the SMTP credentials forward (rotate first — see security finding below) and wire
srv/lib/mail-client.jsto the samesmtpauth.mail.net.saphost, OR - Make a deliberate decision to stop sending these emails and document the change
Security finding (separate issue, not part of #545)
The
cf env imsprodblock also exposes plaintext credentials with no obfuscation:SPRING_MAIL_PASSWORD(SMTP password fordeveloper-rims-notifications)DB_PASSWORD(plaintext password for the legacyIMSDBUSERHANA schema user — direct DB access bypasses HDI)- 3 SAP P-user passwords embedded in
SPRING_APPLICATION_JSONunderapp.tech-users(used for thetech-users-mappingimpersonation pattern at the AEM boundary)
Any SpaceDeveloper on
Developer Destination_IMS / PRODcan read these. Rotation is overdue regardless of #545 outcome, and the new tutorials-srv must not inherit this pattern — the new platform's secrets work (#465 credstore) handles this category. Will file a separate tracking issue.Next on this ticket
Pending confirmation of
isNotificationSendingAllowedvalue in PROD HANA — that determines whether (1), (2), or (3) above holds — and then the design doc.- Working as designed:
- added a commit that references this issue
on Jun 23, 2026
Background
When porting MTA resources from IMS Java to the new CAP tutorials-srv,
tutorials-mailwas carried over fromcom.sap.developers.ims. Investigation today (2026-06-22) during a DEV deploy revealed:EmailSenderServiceImpl,TutorialContributorsNotificationServiceImpl, daily cron at 09:00 UTC with 4 escalating notices to authors of tutorials not reviewed for 6+ months.imsprodinmanifest-prod.yml. The Spring code looks up JNDImail/Session, but nomailservice is bound in the production CF manifest, so the lookup fails.isNotificationSendingAllowedistruein seed/test data — production DB state unverified.EmailNotSentExceptionis caught + logged but never re-thrown — silent failure in cron logs.tutorials-mailMTA resource was carried over but never wired. Removed in the 2026-06-22 deploy since the subaccount has no mail service offering anyway.Task
cf logs imsprod --recent | grep -iE "(email|mail|notif)"to confirm whether the cron is actually firing exceptions, or whether the feature flag in PROD DB isfalseso the cron exits cleanly.srv/jobs/scheduler.jsis the host).Acceptance criteria
docs/superpowers/specs/YYYY-MM-DD-author-nudge-emails-design.mdcovering the 4 options plus recommendation.Out of scope
References
tutorials-mail(.deploy/mta.yamlpre-2026-06-22)application/src/main/java/com/sap/developers/ims/service/email/TutorialContributorsNotificationScheduler.javacron0 0 9 * * ?isNotificationSendingAllowedinImsConfigentityemailListForOutdatedconfig