@@ -24,15 +24,14 @@ module Wire.API.Conversation
24
24
( -- * Conversation
25
25
ConversationMetadata (.. ),
26
26
Conversation (.. ),
27
- mkConversation ,
28
27
cnvType ,
29
28
cnvCreator ,
30
29
cnvAccess ,
31
- cnvAccessRole ,
32
30
cnvName ,
33
31
cnvTeam ,
34
32
cnvMessageTimer ,
35
33
cnvReceiptMode ,
34
+ cnvAccessRoles ,
36
35
ConversationCoverView (.. ),
37
36
ConversationList (.. ),
38
37
ListConversations (.. ),
@@ -46,9 +45,14 @@ module Wire.API.Conversation
46
45
47
46
-- * Conversation properties
48
47
Access (.. ),
49
- AccessRole (.. ),
48
+ AccessRoleV2 (.. ),
49
+ AccessRoleLegacy (.. ),
50
50
ConvType (.. ),
51
51
ReceiptMode (.. ),
52
+ fromAccessRoleLegacy ,
53
+ toAccessRoleLegacy ,
54
+ defRole ,
55
+ maybeRole ,
52
56
53
57
-- * create
54
58
NewConv (.. ),
@@ -90,7 +94,6 @@ import Control.Applicative
90
94
import Control.Lens (at , (?~) )
91
95
import Data.Aeson (FromJSON (.. ), ToJSON (.. ))
92
96
import qualified Data.Aeson as A
93
- import qualified Data.Aeson.Types as A
94
97
import Data.Id
95
98
import Data.List.NonEmpty (NonEmpty )
96
99
import Data.List1
@@ -118,7 +121,7 @@ data ConversationMetadata = ConversationMetadata
118
121
-- FUTUREWORK: Make this a qualified user ID.
119
122
cnvmCreator :: UserId ,
120
123
cnvmAccess :: [Access ],
121
- cnvmAccessRole :: AccessRole ,
124
+ cnvmAccessRoles :: Set AccessRoleV2 ,
122
125
cnvmName :: Maybe Text ,
123
126
-- FUTUREWORK: Think if it makes sense to make the team ID qualified due to
124
127
-- federation.
@@ -130,13 +133,32 @@ data ConversationMetadata = ConversationMetadata
130
133
deriving (Arbitrary ) via (GenericUniform ConversationMetadata )
131
134
deriving (FromJSON , ToJSON ) via Schema ConversationMetadata
132
135
133
- conversationMetadataObjectSchema ::
134
- SchemaP
135
- SwaggerDoc
136
- A. Object
137
- [A. Pair ]
138
- ConversationMetadata
139
- ConversationMetadata
136
+ accessRolesSchema :: ObjectSchema SwaggerDoc (Set AccessRoleV2 )
137
+ accessRolesSchema = toOutput .= accessRolesSchemaTuple `withParser` validate
138
+ where
139
+ toOutput accessRoles = (Just $ toAccessRoleLegacy accessRoles, Just accessRoles)
140
+ validate =
141
+ \ case
142
+ (_, Just v2) -> pure v2
143
+ (Just legacy, Nothing ) -> pure $ fromAccessRoleLegacy legacy
144
+ (Nothing , Nothing ) -> fail " access_role|access_role_v2"
145
+
146
+ accessRolesSchemaOpt :: ObjectSchema SwaggerDoc (Maybe (Set AccessRoleV2 ))
147
+ accessRolesSchemaOpt = toOutput .= accessRolesSchemaTuple `withParser` validate
148
+ where
149
+ toOutput accessRoles = (toAccessRoleLegacy <$> accessRoles, accessRoles)
150
+ validate =
151
+ \ case
152
+ (_, Just v2) -> pure $ Just v2
153
+ (Just legacy, Nothing ) -> pure $ Just (fromAccessRoleLegacy legacy)
154
+ (Nothing , Nothing ) -> pure Nothing
155
+
156
+ accessRolesSchemaTuple :: ObjectSchema SwaggerDoc (Maybe AccessRoleLegacy , Maybe (Set AccessRoleV2 ))
157
+ accessRolesSchemaTuple =
158
+ (,) <$> fst .= optField " access_role" (maybeWithDefault A. Null schema)
159
+ <*> snd .= optField " access_role_v2" (maybeWithDefault A. Null $ set schema)
160
+
161
+ conversationMetadataObjectSchema :: ObjectSchema SwaggerDoc ConversationMetadata
140
162
conversationMetadataObjectSchema =
141
163
ConversationMetadata
142
164
<$> cnvmType .= field " type" schema
@@ -146,18 +168,17 @@ conversationMetadataObjectSchema =
146
168
(description ?~ " The creator's user ID" )
147
169
schema
148
170
<*> cnvmAccess .= field " access" (array schema)
149
- <*> cnvmAccessRole .= field " access_role " schema
171
+ <*> cnvmAccessRoles .= accessRolesSchema
150
172
<*> cnvmName .= optField " name" (maybeWithDefault A. Null schema)
151
173
<* const (" 0.0" :: Text ) .= optional (field " last_event" schema)
152
174
<* const (" 1970-01-01T00:00:00.000Z" :: Text )
153
175
.= optional (field " last_event_time" schema)
154
176
<*> cnvmTeam .= optField " team" (maybeWithDefault A. Null schema)
155
177
<*> cnvmMessageTimer
156
- .= ( optFieldWithDocModifier
157
- " message_timer"
158
- (description ?~ " Per-conversation message timer (can be null)" )
159
- (maybeWithDefault A. Null schema)
160
- )
178
+ .= optFieldWithDocModifier
179
+ " message_timer"
180
+ (description ?~ " Per-conversation message timer (can be null)" )
181
+ (maybeWithDefault A. Null schema)
161
182
<*> cnvmReceiptMode .= optField " receipt_mode" (maybeWithDefault A. Null schema)
162
183
163
184
instance ToSchema ConversationMetadata where
@@ -178,21 +199,6 @@ data Conversation = Conversation
178
199
deriving (Arbitrary ) via (GenericUniform Conversation )
179
200
deriving (FromJSON , ToJSON , S.ToSchema ) via Schema Conversation
180
201
181
- mkConversation ::
182
- Qualified ConvId ->
183
- ConvType ->
184
- UserId ->
185
- [Access ] ->
186
- AccessRole ->
187
- Maybe Text ->
188
- ConvMembers ->
189
- Maybe TeamId ->
190
- Maybe Milliseconds ->
191
- Maybe ReceiptMode ->
192
- Conversation
193
- mkConversation qid ty uid acc role name mems tid ms rm =
194
- Conversation qid (ConversationMetadata ty uid acc role name tid ms rm) mems
195
-
196
202
cnvType :: Conversation -> ConvType
197
203
cnvType = cnvmType . cnvMetadata
198
204
@@ -202,8 +208,8 @@ cnvCreator = cnvmCreator . cnvMetadata
202
208
cnvAccess :: Conversation -> [Access ]
203
209
cnvAccess = cnvmAccess . cnvMetadata
204
210
205
- cnvAccessRole :: Conversation -> AccessRole
206
- cnvAccessRole = cnvmAccessRole . cnvMetadata
211
+ cnvAccessRoles :: Conversation -> Set AccessRoleV2
212
+ cnvAccessRoles = cnvmAccessRoles . cnvMetadata
207
213
208
214
cnvName :: Conversation -> Maybe Text
209
215
cnvName = cnvmName . cnvMetadata
@@ -421,31 +427,108 @@ typeAccess = Doc.string . Doc.enum $ cs . A.encode <$> [(minBound :: Access) ..]
421
427
-- | AccessRoles define who can join conversations. The roles are
422
428
-- "supersets", i.e. Activated includes Team and NonActivated includes
423
429
-- Activated.
424
- data AccessRole
430
+ data AccessRoleLegacy
425
431
= -- | Nobody can be invited to this conversation
426
432
-- (e.g. it's a 1:1 conversation)
427
433
PrivateAccessRole
428
434
| -- | Team-only conversation
429
435
TeamAccessRole
430
436
| -- | Conversation for users who have activated
431
- -- email or phone
437
+ -- email, phone or SSO and bots
432
438
ActivatedAccessRole
433
439
| -- | No checks
434
440
NonActivatedAccessRole
441
+ deriving stock (Eq , Ord , Show , Generic , Enum , Bounded )
442
+ deriving (Arbitrary ) via (GenericUniform AccessRoleLegacy )
443
+ deriving (ToJSON , FromJSON , S.ToSchema ) via Schema AccessRoleLegacy
444
+
445
+ fromAccessRoleLegacy :: AccessRoleLegacy -> Set AccessRoleV2
446
+ fromAccessRoleLegacy = \ case
447
+ PrivateAccessRole -> privateAccessRole
448
+ TeamAccessRole -> teamAccessRole
449
+ ActivatedAccessRole -> activatedAccessRole
450
+ NonActivatedAccessRole -> nonActivatedAccessRole
451
+
452
+ privateAccessRole :: Set AccessRoleV2
453
+ privateAccessRole = Set. fromList []
454
+
455
+ teamAccessRole :: Set AccessRoleV2
456
+ teamAccessRole = Set. fromList [TeamMemberAccessRole ]
457
+
458
+ activatedAccessRole :: Set AccessRoleV2
459
+ activatedAccessRole = Set. fromList [TeamMemberAccessRole , NonTeamMemberAccessRole , ServiceAccessRole ]
460
+
461
+ nonActivatedAccessRole :: Set AccessRoleV2
462
+ nonActivatedAccessRole = Set. fromList [TeamMemberAccessRole , NonTeamMemberAccessRole , GuestAccessRole , ServiceAccessRole ]
463
+
464
+ defRole :: Set AccessRoleV2
465
+ defRole = activatedAccessRole
466
+
467
+ maybeRole :: ConvType -> Maybe (Set AccessRoleV2 ) -> Set AccessRoleV2
468
+ maybeRole SelfConv _ = privateAccessRole
469
+ maybeRole ConnectConv _ = privateAccessRole
470
+ maybeRole One2OneConv _ = privateAccessRole
471
+ maybeRole RegularConv Nothing = defRole
472
+ maybeRole RegularConv (Just r) = r
473
+
474
+ data AccessRoleV2
475
+ = TeamMemberAccessRole
476
+ | NonTeamMemberAccessRole
477
+ | GuestAccessRole
478
+ | ServiceAccessRole
435
479
deriving stock (Eq , Ord , Show , Generic )
436
- deriving (Arbitrary ) via (GenericUniform AccessRole )
437
- deriving (ToJSON , FromJSON , S.ToSchema ) via Schema AccessRole
480
+ deriving (Arbitrary ) via (GenericUniform AccessRoleV2 )
481
+ deriving (ToJSON , FromJSON , S.ToSchema ) via Schema AccessRoleV2
438
482
439
- instance ToSchema AccessRole where
483
+ toAccessRoleLegacy :: Set AccessRoleV2 -> AccessRoleLegacy
484
+ toAccessRoleLegacy accessRoles = do
485
+ fromMaybe NonActivatedAccessRole $ find (allMember accessRoles . fromAccessRoleLegacy) [minBound .. ]
486
+ where
487
+ allMember :: Ord a => Set a -> Set a -> Bool
488
+ allMember rhs lhs = all (`Set.member` lhs) rhs
489
+
490
+ instance ToSchema AccessRoleV2 where
440
491
schema =
441
- (S. schema . description ?~ " Which users can join conversations" ) $
442
- enum @ Text " AccessRole" $
492
+ (S. schema . description ?~ desc) $
493
+ enum @ Text " AccessRoleV2" $
494
+ mconcat
495
+ [ element " team_member" TeamMemberAccessRole ,
496
+ element " non_team_member" NonTeamMemberAccessRole ,
497
+ element " guest" GuestAccessRole ,
498
+ element " service" ServiceAccessRole
499
+ ]
500
+ where
501
+ desc =
502
+ " Which users/services can join conversations.\
503
+ \This replaces the deprecated field `access_role`\
504
+ \and allows for a more fine grained configuration of access roles\
505
+ \in particular a separation of guest and services access."
506
+
507
+ instance ToSchema AccessRoleLegacy where
508
+ schema =
509
+ (S. schema . description ?~ desc) $
510
+ enum @ Text " AccessRoleLegacy" $
443
511
mconcat
444
512
[ element " private" PrivateAccessRole ,
445
513
element " team" TeamAccessRole ,
446
514
element " activated" ActivatedAccessRole ,
447
515
element " non_activated" NonActivatedAccessRole
448
516
]
517
+ where
518
+ desc =
519
+ " Which users can join conversations (deprecated, use `access_role_v2` instead).\
520
+ \Maps to `access_role_v2` as follows:\
521
+ \`private` => `[]` - nobody can be invited to this conversation (e.g. it's a 1:1 conversation)\
522
+ \`team` => `[team_member]` - team-only conversation\
523
+ \`activated` => `[team_member, non_team_member, service]` - conversation for users who have activated email, phone or SSO and services\
524
+ \`non_activated` => `[team_member, non_team_member, service, guest]` - all allowed, no checks\
525
+ \\
526
+ \Maps from `access_role_v2` as follows:\
527
+ \`[]` => `private` - nobody can be invited to this conversation (e.g. it's a 1:1 conversation)\
528
+ \`[team_member]` => `team` - team-only conversation\
529
+ \`[team_member, non_team_member, service]` => `activated` - conversation for users who have activated email, phone or SSO and services\
530
+ \`[team_member, non_team_member, service, guest]` => `non_activated` - all allowed, no checks.\
531
+ \All other configurations of `access_role_v2` are mapped to the smallest superset containing all given access roles."
449
532
450
533
data ConvType
451
534
= RegularConv
@@ -586,7 +669,7 @@ data NewConv = NewConv
586
669
newConvQualifiedUsers :: [Qualified UserId ],
587
670
newConvName :: Maybe Text ,
588
671
newConvAccess :: Set Access ,
589
- newConvAccessRole :: Maybe AccessRole ,
672
+ newConvAccessRoles :: Maybe ( Set AccessRoleV2 ) ,
590
673
newConvTeam :: Maybe ConvTeamInfo ,
591
674
newConvMessageTimer :: Maybe Milliseconds ,
592
675
newConvReceiptMode :: Maybe ReceiptMode ,
@@ -619,7 +702,7 @@ newConvSchema =
619
702
<*> newConvName .= maybe_ (optField " name" schema)
620
703
<*> (Set. toList . newConvAccess)
621
704
.= (fromMaybe mempty <$> optField " access" (Set. fromList <$> array schema))
622
- <*> newConvAccessRole .= maybe_ (optField " access_role " schema)
705
+ <*> newConvAccessRoles .= accessRolesSchemaOpt
623
706
<*> newConvTeam
624
707
.= maybe_
625
708
( optFieldWithDocModifier
@@ -766,7 +849,7 @@ modelConversationUpdateName = Doc.defineModel "ConversationUpdateName" $ do
766
849
767
850
data ConversationAccessData = ConversationAccessData
768
851
{ cupAccess :: Set Access ,
769
- cupAccessRole :: AccessRole
852
+ cupAccessRoles :: Set AccessRoleV2
770
853
}
771
854
deriving stock (Eq , Show , Generic )
772
855
deriving (Arbitrary ) via (GenericUniform ConversationAccessData )
@@ -777,14 +860,14 @@ instance ToSchema ConversationAccessData where
777
860
object " ConversationAccessData" $
778
861
ConversationAccessData
779
862
<$> cupAccess .= field " access" (set schema)
780
- <*> cupAccessRole .= field " access_role " schema
863
+ <*> cupAccessRoles .= accessRolesSchema
781
864
782
865
modelConversationAccessData :: Doc. Model
783
866
modelConversationAccessData = Doc. defineModel " ConversationAccessData" $ do
784
867
Doc. description " Contains conversation properties to update"
785
868
Doc. property " access" (Doc. unique $ Doc. array typeAccess) $
786
869
Doc. description " List of conversation access modes."
787
- Doc. property " access_role" ( Doc. bytes') $
870
+ Doc. property " access_role" Doc. bytes' $
788
871
Doc. description " Conversation access role: private|team|activated|non_activated"
789
872
790
873
data ConversationReceiptModeUpdate = ConversationReceiptModeUpdate
0 commit comments