From 0d7948fe28d10ff934d94f205a2e5e285b8e0739 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Thu, 16 Apr 2026 08:41:34 +0200 Subject: [PATCH 1/3] Changelog. --- .../WPB-24553-send-team_member-join-to-all-apps-in-team | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2-features/WPB-24553-send-team_member-join-to-all-apps-in-team diff --git a/changelog.d/2-features/WPB-24553-send-team_member-join-to-all-apps-in-team b/changelog.d/2-features/WPB-24553-send-team_member-join-to-all-apps-in-team new file mode 100644 index 00000000000..256b7c411f5 --- /dev/null +++ b/changelog.d/2-features/WPB-24553-send-team_member-join-to-all-apps-in-team @@ -0,0 +1 @@ +Send team.member-join to all apps in team. From 402660aec4edb227be02f09424394d329eb7bf05 Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Thu, 16 Apr 2026 09:04:30 +0200 Subject: [PATCH 2/3] Integration test. --- integration/test/API/Brig.hs | 4 ++-- integration/test/Test/Apps.hs | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/integration/test/API/Brig.hs b/integration/test/API/Brig.hs index cecb6fbef73..d5cc1d33bf9 100644 --- a/integration/test/API/Brig.hs +++ b/integration/test/API/Brig.hs @@ -1227,11 +1227,11 @@ data NewApp = NewApp instance Default NewApp where def = NewApp - { name = "", + { name = "default name", assets = Nothing, accentId = Nothing, category = "other", - description = "" + description = "default description" } createApp :: (MakesValue creator) => creator -> String -> NewApp -> App Response diff --git a/integration/test/Test/Apps.hs b/integration/test/Test/Apps.hs index 9f0c3f3845b..72eaa5e637f 100644 --- a/integration/test/Test/Apps.hs +++ b/integration/test/Test/Apps.hs @@ -26,6 +26,7 @@ import API.Galley import Control.Lens hiding ((.=)) import Data.Aeson.QQ.Simple import MLS.Util +import Notifications import SetupHelpers import Testlib.Prelude @@ -538,3 +539,25 @@ testRemoveServicesAccessRole = do memberIds <- mapM (\m -> m %. "qualified_id.id" >>= asString) members appId <- app %. "qualified_id.id" & asString memberIds `shouldNotContain` [appId] + +testAppReceivesMemberJoinNotification :: (HasCallStack) => App () +testAppReceivesMemberJoinNotification = do + (owner, tid, []) <- createTeam OwnDomain 1 + + -- Create an app in the team + app <- bindResponse (createApp owner tid def) $ \resp -> do + resp.status `shouldMatchInt` 200 + resp.json %. "user" + + -- With websockets open for both owner and app, add a new regular member. + -- Both should receive the team.member-join notification. + withWebSockets [owner, app] $ \[wsOwner, wsApp] -> do + newMember <- addUserToTeam owner + + memberJoinOwner <- awaitMatch isTeamMemberJoinNotif wsOwner + memberJoinOwner %. "payload.0.team" `shouldMatch` tid + memberJoinOwner %. "payload.0.data.user" `shouldMatch` objId newMember + + memberJoinApp <- awaitMatch isTeamMemberJoinNotif wsApp + memberJoinApp %. "payload.0.team" `shouldMatch` tid + memberJoinApp %. "payload.0.data.user" `shouldMatch` objId newMember From 3dedc4440edee6165576651676ec18f649e426aa Mon Sep 17 00:00:00 2001 From: Matthias Fischmann Date: Thu, 16 Apr 2026 09:17:34 +0200 Subject: [PATCH 3/3] Implementation. --- services/galley/src/Galley/API/Teams.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/services/galley/src/Galley/API/Teams.hs b/services/galley/src/Galley/API/Teams.hs index d6c0850d775..fbf7ed1b748 100644 --- a/services/galley/src/Galley/API/Teams.hs +++ b/services/galley/src/Galley/API/Teams.hs @@ -119,6 +119,7 @@ import Wire.API.Team.SearchVisibility import Wire.API.Team.SearchVisibility qualified as Public import Wire.API.Team.Size import Wire.API.User qualified as U +import Wire.BrigAPIAccess import Wire.BrigAPIAccess qualified as Brig import Wire.BrigAPIAccess qualified as E import Wire.CodeStore @@ -1153,10 +1154,11 @@ addTeamMemberInternal tid origin originConn (ntmNewTeamMember -> new) = do E.createTeamMember tid new now <- Now.get + appIds <- getAppIdsForTeam tid let e = newEvent tid now (EdMemberJoin (new ^. userId)) let recipients = case origin of - Just o -> userRecipient <$> o : filter (/= o) ((new ^. userId) : admins') - Nothing -> userRecipient <$> new ^. userId : admins' + Just o -> userRecipient <$> o : filter (/= o) ((new ^. userId) : admins' ++ appIds) + Nothing -> userRecipient <$> (new ^. userId) : admins' ++ appIds pushNotifications [ def { origin = Just (new ^. userId),