Add SmartTwin event subscriber skeleton#1936
Draft
pvankouteren wants to merge 18 commits into
Draft
Conversation
Introduce AccountVerified event (dispatched from AccountObserver when email_verified_at transitions from null to a datetime) and a SmartTwinEventSubscriber that maps AccountVerified and Spatie's RoleAttached to per-role CreateUserAccount / CreateCoachAccount jobs. Job handlers are intentionally left empty — implementation of the SmartTwin API calls comes in a follow-up.
baf5404 to
189b15e
Compare
Generalize UserDeleted second payload from $accountRelated to $context, build it inline in UserService (no longer depends on EconobisService), re-shape locally in EconobisEventSubscriber, and add SmartTwin DeleteAccount job dispatched from SmartTwinEventSubscriber::handleUserDeleted.
Guzzle-based client with toggleable PSR-3 log middleware, X-Api-Key header auth, and a User resource covering the Account API's register and delete endpoints.
CreateUserAccount, CreateCoachAccount, and DeleteAccount now call SmartTwinApi via DI. The create jobs persist the returned userId on User.extra.smarttwin_user_id and skip when it is already set; all three short-circuit when SmartTwin calls are disabled.
…be commands - Add SMARTTWIN_API_SIGN_KEY and SMARTTWIN_PREVIOUS_API_SIGN_KEY (comma-separated) config values for zero-downtime key rotation - Add SmartTwinSigned middleware checking X-Webhook-ApiKey header against current and previous sign keys - Register POST api/v1/smarttwin route (api.v1.smarttwin.store) with SmartTwinSigned middleware - Add SmartTwinController that logs incoming webhook JSON - Add EventSubscription resource with subscribe/unsubscribe methods - Add api:smarttwin:subscribe command (logs subscriptionId via Log::info) - Add api:smarttwin:unsubscribe command (accepts subscriptionId arg or reads from config) - Add SMARTTWIN_SUBSCRIPTION_ID config value to persist subscription ID
json_decode returns false (not null) for a JSON 'false' response body, so the ?? [] fallback did not trigger. Use is_array() instead.
…twin-key-rotation
…ponse The SmartTwin API currently returns the subscription ID under "Value" instead of "subscriptionId" as documented. Check subscriptionId first, fall back to Value until the API is corrected.
- Move SmartTwinSigned from Middleware/ to Middleware/Api/ for consistency - Update bootstrap/app.php alias to new namespace - Add App\Enums\SmartTwin\EventType with RESIDENT_SCAN_FINISHED and COACH_SCAN_FINISHED
- Add SHORT_SMARTTWIN_DOSSIER_ID constant to BuildingSettingHelper - SmartTwinController now resolves the building from DossierId via building_settings, appends the webhook data payload to buildings.smarttwin_callback
…ding ID
- Job now implements ShouldBeUnique with uniqueId() returning "{EventType}_{buildingId}"
- Add buildingId as constructor parameter alongside callbackData
- Update observer and fallback cron command to pass building ID on dispatch
- Add Client::get() method for GET requests - Add Advice resource with getAdvisorToolResults() and getQuickScanResults() - Add SmartTwinApi::advice() returning the Advice resource - Add SmartTwinService::processResults() stub - Implement GetAdviceResults job: resolves endpoint via EventType enum, calls service, then removes the processed callback from buildings.smarttwin_callback
…rgument - Add optional buildingId argument to api:smarttwin:get-advice-results command to target a single building; omit for all pending buildings (via ->when()) - Register command in console.php to run nightly at 03:00 with withoutOverlapping()
- Add @Property array|null $smarttwin_callback to Building docblock to fix assign.propertyType errors in controller and job - Apply phpcbf code style fixes
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.
Summary
Scaffolds the integration plumbing between this app and the future SmartTwin external API. No actual SmartTwin calls are made yet — this PR only wires up the events, listener and job skeletons so they can be filled in later.
App\Events\AccountVerified— dispatched fromAccountObserver::updated()whenemail_verified_attransitions fromnullto a datetime. Covers every verification path (Fortify email link, admin-create auto-verify, API V1, SuperAdmin manual confirm).App\Listeners\SmartTwinEventSubscriber— registered inAppServiceProvider::attachSubscribers(). Listens for:AccountVerified→ iterates the account's users/roles and dispatches the matching job per (user, role) forresidentandcoach.Spatie\Permission\Events\RoleAttached→ only acts when the underlying account is already verified (avoids double-dispatch withAccountVerifiedfor fresh registrations); dispatches the matching job per attached role.App\Jobs\SmartTwin\Out\CreateUserAccountandCreateCoachAccount—ShouldQueuejobs on theapp_externalqueue, with emptyhandle()bodies as placeholders for the real SmartTwin API calls.Why two triggers
AccountVerifiedandRoleAttachedare designed to be each other's complement:AccountVerifiedis the trigger (RoleAttachedhad skipped because the account wasn't verified yet).RoleAttachedis the trigger.Because the SmartTwin API takes
(user_id, role), each(User, role)pair maps to a distinct SmartTwin account — so dispatching per role rather than per account keeps idempotency concerns minimal.Out of scope
RoleDetachedhandling.Test plan
AppServiceProvider::attachSubscribers()).AccountVerifiedfires when an unverified account becomes verified (Fortify link, admin save, API V1, SuperAdmin confirm).RoleAttachedhandler skips while the account is unverified and dispatches once the account is verified.app_externalqueue.