feat(intent): resolve cross-model notify recipients + surface dropped glue (#6360)#6391
Merged
Merged
Conversation
… glue (#6360) A `schedules.notify` / `notifications` recipient (`to`) or `{placeholder}` that is a one-hop `relation.field` could only reference a LOCAL entity: for a cross-model relation the target was looked up in the model's own byName map, resolved to null, and the whole notification/schedule was silently skipped with only a server-side LOGGER.warn. Cross-model to-one relations are first-class everywhere else (dropdowns, generate targets resolve through CrossModelSupport), so a recipient like `Partner.email` where Partner is owned by another `uses` model should resolve the same way. Two changes: 1. Cross-model recipient resolution. NotificationSupport gains an injected CrossModelLookup (kept Spring/IO-free; the caller's lambda does the IO via CrossModelSupport) so a cross-model `relation.field` resolves against the owner model, validates the field against the owner's properties, and records a RelationLoad flagged crossModel with the owner alias/project. The generated listener/job then imports the OWNER's gen Entity/Repository (the same registry-wide-compile mechanism the relation links / personal assignee already use) - Job.java.template / Notification.java.template import from ${load.javaGenFolder}, which generateUtils picks from the owner alias for a cross-model load and this project's folder otherwise. 2. Dropped glue is no longer silent at the API level. A recipient that cannot be resolved is recorded on the generation context and returned in the generate response under "warnings" (the generation still succeeds), in addition to the log line - so an author sees that a notification/schedule was NOT emitted. Verified end-to-end on a running instance with a two-model fixture (owner Partner{email}, consumer Deal with a cross-model Partner relation): the emitted DueDealsJob / DealCreatedNotification import gen.<owner>.data.partner.Partner* and read Partner.Email; the client-Java batch compiled and registered the job + listener (0 javac errors); a bogus recipient surfaced in the generate response "warnings" and its schedule was dropped. Unit coverage added in NotificationSupportTest (cross-model resolve, owner-field validation, no-lookup fallback). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #6360.
Problem
A
schedules.notify/notificationsrecipient (to) or{placeholder}that is a one-hoprelation.fieldonly resolved against local entities. For a cross-model relation (Partner.emailwherePartneris owned by anotherusesmodel) the target lookup returned null and the entire notification/schedule was silently skipped with only a server-sideLOGGER.warn. Cross-model to-one relations are first-class everywhere else (dropdowns,generatestargets resolve throughCrossModelSupport).Changes
NotificationSupportgains an injectedCrossModelLookup(kept Spring/IO-free; the caller's lambda does the IO viaCrossModelSupport). A cross-modelrelation.fieldresolves against the owner model, validates the field against the owner's property names, and records aRelationLoadflaggedcrossModelcarrying the owner alias/project. The generated listener/job imports the owner's genEntity/Repository(the registry-wide-compile mechanism the relation links / personal assignee already use):Job.java.template/Notification.java.templatenow import from${load.javaGenFolder}, whichgenerateUtilsderives from the owner alias for a cross-model load and this project's folder otherwise.warnings(generation still succeeds), in addition to the log line.Verification (outermost layer)
Two-model fixture on a running instance (owner
Partner{email}, consumerDealwith a cross-modelPartnerrelation + a notification and a scheduled notify toPartner.email, plus one bogus recipient):DueDealsJob.java/DealCreatedNotification.javaimportgen.xm_owner.data.partner.Partner{Entity,Repository}and readPartner.Email/Partner.Name;Registered client-Java job [gen.events.DueDealsJob]) and the listener (@Listener [gen.events.DealCreatedNotification] connected to TOPIC), 0 javac errors;warningsand its schedule was dropped.Unit coverage added in
NotificationSupportTest(cross-model resolve, owner-field validation, no-lookup fallback). ExistingNotificationSupportTest+GlueSchedulesTestgreen.🤖 Generated with Claude Code