diff --git a/cassandra-schema.cql b/cassandra-schema.cql index c069ebc9f98..01160b1ca6d 100644 --- a/cassandra-schema.cql +++ b/cassandra-schema.cql @@ -694,6 +694,7 @@ CREATE TABLE brig_test.user ( supported_protocols int, team uuid, text_status text, + user_type int, write_time_bumper int ) WITH bloom_filter_fp_chance = 0.1 AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'} diff --git a/changelog.d/0-release-notes/WPB-22549-apps-vs-convs b/changelog.d/0-release-notes/WPB-22549-apps-vs-convs new file mode 100644 index 00000000000..ac34d33820c --- /dev/null +++ b/changelog.d/0-release-notes/WPB-22549-apps-vs-convs @@ -0,0 +1 @@ +Cassandra (`brig.user`) now keeps track of user types, only for newly created users. **Read this paragraph if you have already created apps before their official support:** For existing users and bots, the user type is inferred, but existing apps will show as regular users. Please remove those users from your team and create them again. diff --git a/libs/wire-api/src/Wire/API/User.hs b/libs/wire-api/src/Wire/API/User.hs index defa322a590..4800869efad 100644 --- a/libs/wire-api/src/Wire/API/User.hs +++ b/libs/wire-api/src/Wire/API/User.hs @@ -470,7 +470,7 @@ instance (1 <= max) => ToJSON (LimitedQualifiedUserIdList max) where -- UserType data UserType = UserTypeRegular | UserTypeApp | UserTypeBot - deriving (Eq, Show, Generic) + deriving (Eq, Ord, Show, Generic) deriving (Arbitrary) via (GenericUniform UserType) deriving (A.FromJSON, A.ToJSON) via (Schema UserType) @@ -486,6 +486,20 @@ instance ToSchema UserType where Schema.element "bot" UserTypeBot ] +instance C.Cql UserType where + ctype = C.Tagged C.IntColumn + + toCql UserTypeRegular = C.CqlInt 0 + toCql UserTypeBot = C.CqlInt 1 + toCql UserTypeApp = C.CqlInt 2 + + fromCql (C.CqlInt i) = case i of + 0 -> pure UserTypeRegular + 1 -> pure UserTypeBot + 2 -> pure UserTypeApp + n -> Left $ "unexpected user type: " ++ show n + fromCql _ = Left "user type: int expected" + -------------------------------------------------------------------------------- -- UserProfile @@ -579,6 +593,7 @@ instance FromJSON SelfProfile where -- | The data of an existing user. data User = User { userQualifiedId :: Qualified UserId, + userType :: UserType, -- | User identity. For endpoints like @/self@, it will be present in the response iff -- the user is activated, and the email/phone contained in it will be guaranteedly -- verified. {#RefActivation} @@ -635,6 +650,8 @@ userObjectSchema = User <$> userQualifiedId .= field "qualified_id" schema + <*> userType + .= field "type" schema <* userId .= optional (field "id" (deprecatedSchema "qualified_id" schema)) <*> userIdentity .= maybeUserIdentityObjectSchema diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/SelfProfile_user.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/SelfProfile_user.hs index 608fd5a96af..44eab4b0d5d 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/SelfProfile_user.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/SelfProfile_user.hs @@ -41,6 +41,7 @@ testObject_SelfProfile_user_1 = { qUnqualified = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000002")), qDomain = Domain {_domainText = "n0-994.m-226.f91.vg9p-mj-j2"} }, + userType = UserTypeRegular, userIdentity = Just (EmailIdentity (unsafeEmailAddress "some" "example")), userEmailUnvalidated = Nothing, userDisplayName = Name {fromName = "@\1457\2598\66242\US\1104967l+\137302\&6\996495^\162211Mu\t"}, diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/User_user.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/User_user.hs index 3d46bf5bf94..cfcb9b8d30f 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/User_user.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/User_user.hs @@ -54,6 +54,7 @@ testObject_User_user_1 = { qUnqualified = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000200000002")), qDomain = Domain {_domainText = "s-f4.s"} }, + userType = UserTypeRegular, userIdentity = Nothing, userEmailUnvalidated = Nothing, userDisplayName = Name {fromName = "\NULuv\996028su\28209lRi"}, @@ -80,6 +81,7 @@ testObject_User_user_2 = { qUnqualified = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000200000001")), qDomain = Domain {_domainText = "k.vbg.p"} }, + userType = UserTypeBot, userIdentity = Just (EmailIdentity (unsafeEmailAddress "some" "example")), userEmailUnvalidated = Nothing, userDisplayName = @@ -120,6 +122,7 @@ testObject_User_user_3 = { qUnqualified = Id (fromJust (UUID.fromString "00000002-0000-0000-0000-000100000002")), qDomain = Domain {_domainText = "dt.n"} }, + userType = UserTypeRegular, userIdentity = Just (EmailIdentity (unsafeEmailAddress "some" "example")), userEmailUnvalidated = Nothing, userDisplayName = @@ -153,6 +156,7 @@ testObject_User_user_4 = { qUnqualified = Id (fromJust (UUID.fromString "00000000-0000-0002-0000-000200000002")), qDomain = Domain {_domainText = "28b.cqb"} }, + userType = UserTypeRegular, userIdentity = Just (SSOIdentity (UserScimExternalId "") (Just (unsafeEmailAddress "some" "example"))), userEmailUnvalidated = Nothing, @@ -191,6 +195,7 @@ testObject_User_user_5 = { qUnqualified = Id (fromJust (UUID.fromString "00000000-0000-0002-0000-000200000002")), qDomain = Domain {_domainText = "28b.cqb"} }, + userType = UserTypeRegular, userIdentity = Just (EmailIdentity (unsafeEmailAddress "some" "example")), userEmailUnvalidated = Nothing, diff --git a/libs/wire-api/test/golden/Test/Wire/API/Golden/Manual/UserEvent.hs b/libs/wire-api/test/golden/Test/Wire/API/Golden/Manual/UserEvent.hs index 6a6ea459187..50c60904d95 100644 --- a/libs/wire-api/test/golden/Test/Wire/API/Golden/Manual/UserEvent.hs +++ b/libs/wire-api/test/golden/Test/Wire/API/Golden/Manual/UserEvent.hs @@ -222,6 +222,7 @@ alice = { qUnqualified = Id (fromJust (UUID.fromString "539d9183-32a5-4fc4-ba5c-4634454e7585")), qDomain = Domain {_domainText = "foo.example.com"} }, + userType = UserTypeRegular, userIdentity = Nothing, userEmailUnvalidated = Nothing, userDisplayName = Name "alice", @@ -252,6 +253,7 @@ bob = { qUnqualified = Id (fromJust (UUID.fromString "284d1c86-5117-4c58-aa18-c0068f3f7d8c")), qDomain = Domain {_domainText = "baz.example.com"} }, + userType = UserTypeRegular, userIdentity = Nothing, userEmailUnvalidated = Nothing, userDisplayName = Name "bob", diff --git a/libs/wire-api/test/golden/testObject_SelfProfile_user_1.json b/libs/wire-api/test/golden/testObject_SelfProfile_user_1.json index a90f6312654..0da295244f9 100644 --- a/libs/wire-api/test/golden/testObject_SelfProfile_user_1.json +++ b/libs/wire-api/test/golden/testObject_SelfProfile_user_1.json @@ -23,5 +23,6 @@ ], "team": "00000001-0000-0002-0000-000000000002", "text_status": "text status", - "searchable": true + "searchable": true, + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_UserEvent_1.json b/libs/wire-api/test/golden/testObject_UserEvent_1.json index 45e4ce4e178..f2b5b821e9c 100644 --- a/libs/wire-api/test/golden/testObject_UserEvent_1.json +++ b/libs/wire-api/test/golden/testObject_UserEvent_1.json @@ -19,6 +19,7 @@ ], "team": "bb843450-b2f5-4ec8-90bd-52c7d5f1d22e", "text_status": "text status", - "searchable": true + "searchable": true, + "type": "regular" } } diff --git a/libs/wire-api/test/golden/testObject_UserEvent_2.json b/libs/wire-api/test/golden/testObject_UserEvent_2.json index ec35de96dc4..74180f5dea8 100644 --- a/libs/wire-api/test/golden/testObject_UserEvent_2.json +++ b/libs/wire-api/test/golden/testObject_UserEvent_2.json @@ -19,6 +19,7 @@ ], "team": "bb843450-b2f5-4ec8-90bd-52c7d5f1d22e", "text_status": "text status", - "searchable": true + "searchable": true, + "type": "regular" } } diff --git a/libs/wire-api/test/golden/testObject_User_user_1.json b/libs/wire-api/test/golden/testObject_User_user_1.json index 699a40af49a..0b153c17495 100644 --- a/libs/wire-api/test/golden/testObject_User_user_1.json +++ b/libs/wire-api/test/golden/testObject_User_user_1.json @@ -15,5 +15,6 @@ "supported_protocols": [ "proteus" ], - "searchable": true + "searchable": true, + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_User_user_2.json b/libs/wire-api/test/golden/testObject_User_user_2.json index 0cedbbc3e91..ea77c4a8add 100644 --- a/libs/wire-api/test/golden/testObject_User_user_2.json +++ b/libs/wire-api/test/golden/testObject_User_user_2.json @@ -35,5 +35,6 @@ "status": "deleted", "supported_protocols": [], "text_status": "text status", - "searchable": true + "searchable": true, + "type": "bot" } diff --git a/libs/wire-api/test/golden/testObject_User_user_3.json b/libs/wire-api/test/golden/testObject_User_user_3.json index b8efaee771b..f5ad7cce79c 100644 --- a/libs/wire-api/test/golden/testObject_User_user_3.json +++ b/libs/wire-api/test/golden/testObject_User_user_3.json @@ -23,5 +23,6 @@ "proteus" ], "team": "00000002-0000-0001-0000-000200000000", - "searchable": true + "searchable": true, + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_User_user_4.json b/libs/wire-api/test/golden/testObject_User_user_4.json index ddec3da3945..ef579f758db 100644 --- a/libs/wire-api/test/golden/testObject_User_user_4.json +++ b/libs/wire-api/test/golden/testObject_User_user_4.json @@ -25,5 +25,6 @@ "proteus" ], "team": "00000000-0000-0000-0000-000100000002", - "searchable": true + "searchable": true, + "type": "regular" } diff --git a/libs/wire-api/test/golden/testObject_User_user_5.json b/libs/wire-api/test/golden/testObject_User_user_5.json index bde3ac25cc7..8a934426b23 100644 --- a/libs/wire-api/test/golden/testObject_User_user_5.json +++ b/libs/wire-api/test/golden/testObject_User_user_5.json @@ -22,5 +22,6 @@ "proteus" ], "team": "00000000-0000-0000-0000-000100000002", - "searchable": true + "searchable": true, + "type": "regular" } diff --git a/libs/wire-subsystems/src/Wire/AppSubsystem/Interpreter.hs b/libs/wire-subsystems/src/Wire/AppSubsystem/Interpreter.hs index d591ecbc44f..70081a4e12f 100644 --- a/libs/wire-subsystems/src/Wire/AppSubsystem/Interpreter.hs +++ b/libs/wire-subsystems/src/Wire/AppSubsystem/Interpreter.hs @@ -237,6 +237,7 @@ appNewStoredUser creator new = do pure NewStoredUser { id = Id uid, + userType = UserTypeApp, email = Nothing, ssoId = Nothing, name = new.name, diff --git a/libs/wire-subsystems/src/Wire/StoredUser.hs b/libs/wire-subsystems/src/Wire/StoredUser.hs index 47edfd8c6ea..78e3cbc42c0 100644 --- a/libs/wire-subsystems/src/Wire/StoredUser.hs +++ b/libs/wire-subsystems/src/Wire/StoredUser.hs @@ -39,6 +39,7 @@ import Wire.Arbitrary data StoredUser = StoredUser { id :: UserId, + userType :: Maybe UserType, name :: Name, textStatus :: Maybe TextStatus, pict :: Maybe Pict, @@ -100,6 +101,7 @@ mkUserFromStored domain defaultLocale storedUser = svc = newServiceRef <$> storedUser.serviceId <*> storedUser.providerId in User { userQualifiedId = (Qualified storedUser.id domain), + userType = inferUserType (isJust svc) storedUser.userType, userIdentity = storedUser.identity, userEmailUnvalidated = storedUser.emailUnvalidated, userDisplayName = storedUser.name, @@ -120,26 +122,48 @@ mkUserFromStored domain defaultLocale storedUser = userSearchable = (fromMaybe True storedUser.searchable) } +-- | This function helps us avoid data migrations in cassandra: we +-- allow `User`s to have no type value for backwards compatibility. +-- The type is inferred as "bot" if there is a serviceId, and +-- "regular" otherwise. For newly created apps, the second argument +-- will always be `Just`. +inferUserType :: Bool {- is service -} -> Maybe UserType -> UserType +inferUserType True _ = UserTypeBot +inferUserType False Nothing = UserTypeRegular +inferUserType False (Just t) = t + toLocale :: Locale -> (Maybe Language, Maybe Country) -> Locale toLocale _ (Just l, c) = Locale l c toLocale l _ = l +toIdentity :: + Maybe EmailAddress -> + Maybe UserSSOId -> + Maybe UserIdentity +toIdentity (Just e) Nothing = Just $! EmailIdentity e +toIdentity email (Just ssoid) = Just $! SSOIdentity ssoid email +toIdentity Nothing Nothing = Nothing + -- | If the user is not activated, 'toIdentity' will return 'Nothing', because -- elsewhere we rely on the fact that a non-empty 'UserIdentity' means that the -- user is activated. -toIdentity :: +-- +-- FUTUREWORK(fisx): our account status representation is a mess. we +-- have `activated` for personal users that have an invitation +-- pending; `status` for team members; sometimes no user identity +-- means not activated; and maybe more exceptions that i haven't found +-- today? this needs to be cleaned up! +toIdentityIfActivated :: -- | Whether the user is activated Bool -> Maybe EmailAddress -> Maybe UserSSOId -> Maybe UserIdentity -toIdentity True (Just e) Nothing = Just $! EmailIdentity e -toIdentity True email (Just ssoid) = Just $! SSOIdentity ssoid email -toIdentity True Nothing Nothing = Nothing -toIdentity False _ _ = Nothing +toIdentityIfActivated True e s = toIdentity e s +toIdentityIfActivated False _ _ = Nothing instance HasField "identity" StoredUser (Maybe UserIdentity) where - getField user = toIdentity user.activated user.email user.ssoId + getField user = toIdentityIfActivated user.activated user.email user.ssoId instance HasField "locale" StoredUser (Maybe Locale) where getField user = Locale <$> user.language <*> pure user.country @@ -148,6 +172,7 @@ instance HasField "locale" StoredUser (Maybe Locale) where data NewStoredUser = NewStoredUser { id :: UserId, + userType :: UserType, name :: Name, textStatus :: Maybe TextStatus, pict :: Pict, @@ -180,6 +205,7 @@ recordInstance ''NewStoredUser deriving instance Show ( UserId, + UserType, Name, Maybe TextStatus, Pict, @@ -205,12 +231,14 @@ deriving instance instance HasField "service" NewStoredUser (Maybe ServiceRef) where getField user = ServiceRef <$> user.serviceId <*> user.providerId +-- This saves the identity from `NewStoredUser` even if the user is +-- not activated. newStoredUserToUser :: Qualified NewStoredUser -> User newStoredUserToUser (Qualified new domain) = User { userQualifiedId = Qualified new.id domain, - -- save identity even if the user is not activated - userIdentity = toIdentity True new.email new.ssoId, + userType = new.userType, + userIdentity = toIdentity new.email new.ssoId, userEmailUnvalidated = Nothing, userDisplayName = new.name, userTextStatus = new.textStatus, diff --git a/libs/wire-subsystems/src/Wire/UserStore/Cassandra.hs b/libs/wire-subsystems/src/Wire/UserStore/Cassandra.hs index 7a854c4ce23..4d22c715189 100644 --- a/libs/wire-subsystems/src/Wire/UserStore/Cassandra.hs +++ b/libs/wire-subsystems/src/Wire/UserStore/Cassandra.hs @@ -442,10 +442,10 @@ lookupServiceUsersForTeamImpl pid sid tid mPagingState = insertUser :: PrepQuery W (TupleType NewStoredUser) () insertUser = - "INSERT INTO user (id, name, text_status, picture, assets, email, sso_id, \ + "INSERT INTO user (id, user_type, name, text_status, picture, assets, email, sso_id, \ \accent_id, password, activated, status, expires, language, \ \country, provider, service, handle, team, managed_by, supported_protocols, searchable) \ - \VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + \VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" insertServiceUser :: PrepQuery W (ProviderId, ServiceId, BotId, ConvId, Maybe TeamId) () insertServiceUser = @@ -460,7 +460,7 @@ insertServiceTeam = selectUsers :: PrepQuery R (Identity [UserId]) (TupleType StoredUser) selectUsers = [sql| - SELECT id, name, text_status, picture, email, email_unvalidated, sso_id, accent_id, assets, + SELECT id, user_type, name, text_status, picture, email, email_unvalidated, sso_id, accent_id, assets, activated, status, expires, language, country, provider, service, handle, team, managed_by, supported_protocols, searchable FROM user WHERE id IN ? diff --git a/libs/wire-subsystems/test/unit/Wire/MiniBackend.hs b/libs/wire-subsystems/test/unit/Wire/MiniBackend.hs index fbd01bdd5b6..1a8839f00dd 100644 --- a/libs/wire-subsystems/test/unit/Wire/MiniBackend.hs +++ b/libs/wire-subsystems/test/unit/Wire/MiniBackend.hs @@ -138,7 +138,7 @@ newtype PendingNotEmptyIdentityStoredUser = PendingNotEmptyIdentityStoredUser St instance Arbitrary PendingNotEmptyIdentityStoredUser where arbitrary = do - user <- arbitrary `suchThat` \user -> isJust user.identity + user <- arbitrary `suchThat` \user -> isJust user.identity && user.userType /= Just UserTypeApp pure $ PendingNotEmptyIdentityStoredUser (user {status = Just PendingInvitation}) newtype NotPendingEmptyIdentityStoredUser = NotPendingEmptyIdentityStoredUser StoredUser @@ -147,7 +147,7 @@ newtype NotPendingEmptyIdentityStoredUser = NotPendingEmptyIdentityStoredUser St -- TODO: make sure this is a valid state instance Arbitrary NotPendingEmptyIdentityStoredUser where arbitrary = do - user <- arbitrary `suchThat` \user -> isNothing user.identity + user <- arbitrary `suchThat` \user -> isNothing user.identity && user.userType /= Just UserTypeApp notPendingStatus <- elements (Nothing : map Just [Active, Suspended, Ephemeral]) pure $ NotPendingEmptyIdentityStoredUser (user {status = notPendingStatus}) @@ -164,7 +164,7 @@ newtype NotPendingStoredUser = NotPendingStoredUser StoredUser instance Arbitrary NotPendingStoredUser where arbitrary = do - user <- arbitrary `suchThat` \user -> isJust user.identity + user <- arbitrary `suchThat` \user -> isJust user.identity && user.userType /= Just UserTypeApp notPendingStatus <- elements (Nothing : map Just [Active, Suspended, Ephemeral]) pure $ NotPendingStoredUser (user {status = notPendingStatus}) @@ -173,7 +173,7 @@ newtype NotPendingSSOIdWithEmailStoredUser = NotPendingSSOIdWithEmailStoredUser instance Arbitrary NotPendingSSOIdWithEmailStoredUser where arbitrary = do - user <- arbitrary `suchThat` \user -> fmap isUserSSOId user.ssoId == Just True + user <- arbitrary `suchThat` isSsoIsNotApp notPendingStatus <- elements (Nothing : map Just [Active, Suspended, Ephemeral]) e <- arbitrary pure $ @@ -184,6 +184,10 @@ instance Arbitrary NotPendingSSOIdWithEmailStoredUser where email = Just e } ) + where + isSsoIsNotApp user = + fmap isUserSSOId user.ssoId == Just True + && user.userType /= Just UserTypeApp newtype ActiveStoredUser = ActiveStoredUser StoredUser deriving (Show, Eq) diff --git a/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserStore.hs b/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserStore.hs index 1aa38695158..cfb64ecaf54 100644 --- a/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserStore.hs +++ b/libs/wire-subsystems/test/unit/Wire/MockInterpreters/UserStore.hs @@ -198,6 +198,7 @@ newStoredUserToStoredUser :: NewStoredUser -> StoredUser newStoredUserToStoredUser new = StoredUser { id = new.id, + userType = Just new.userType, name = new.name, textStatus = new.textStatus, pict = Just new.pict, diff --git a/services/brig/brig.cabal b/services/brig/brig.cabal index be188db9d0e..b7c0c335249 100644 --- a/services/brig/brig.cabal +++ b/services/brig/brig.cabal @@ -190,6 +190,7 @@ library Brig.Schema.V89_UpdateDomainRegistrationSchema Brig.Schema.V90_DomainRegistrationTeamIndex Brig.Schema.V91_UpdateDomainRegistrationSchema_AddWebappUrl + Brig.Schema.V92_AddUserType Brig.Team.API Brig.Team.Template Brig.Template diff --git a/services/brig/src/Brig/Data/User.hs b/services/brig/src/Brig/Data/User.hs index 59d69fab06e..fe176284211 100644 --- a/services/brig/src/Brig/Data/User.hs +++ b/services/brig/src/Brig/Data/User.hs @@ -88,6 +88,7 @@ newStoredUser u inv tid mbHandle = do user uid l e mPassword = NewStoredUser { id = uid, + userType = UserTypeRegular, email = ident >>= emailIdentity, ssoId = ident >>= ssoIdentity, name, @@ -117,6 +118,7 @@ newStoredUserViaScim uid externalId tid locale name email = do pure $ NewStoredUser { id = uid, + userType = UserTypeRegular, email = Just email, ssoId = Just (UserScimExternalId externalId), name, diff --git a/services/brig/src/Brig/Provider/API.hs b/services/brig/src/Brig/Provider/API.hs index a57186d6a71..dae3599c9ce 100644 --- a/services/brig/src/Brig/Provider/API.hs +++ b/services/brig/src/Brig/Provider/API.hs @@ -820,6 +820,7 @@ addBot zuid zcon cid add = do let usr = NewStoredUser { id = botUserId bid, + userType = UserTypeBot, name, textStatus = Nothing, email = Nothing, diff --git a/services/brig/src/Brig/Schema/Run.hs b/services/brig/src/Brig/Schema/Run.hs index c13d35287b4..bef0e82ce37 100644 --- a/services/brig/src/Brig/Schema/Run.hs +++ b/services/brig/src/Brig/Schema/Run.hs @@ -66,6 +66,7 @@ import Brig.Schema.V88_DomainRegistrationTable qualified as V88_DomainRegistrati import Brig.Schema.V89_UpdateDomainRegistrationSchema qualified as V89_UpdateDomainRegistrationSchema import Brig.Schema.V90_DomainRegistrationTeamIndex qualified as V90_DomainRegistrationTeamIndex import Brig.Schema.V91_UpdateDomainRegistrationSchema_AddWebappUrl qualified as V91_UpdateDomainRegistrationSchema_AddWebappUrl +import Brig.Schema.V92_AddUserType qualified as V92_AddUserType import Cassandra.MigrateSchema (migrateSchema) import Cassandra.Schema import Control.Exception (finally) @@ -138,7 +139,8 @@ migrations = V88_DomainRegistrationTable.migration, V89_UpdateDomainRegistrationSchema.migration, V90_DomainRegistrationTeamIndex.migration, - V91_UpdateDomainRegistrationSchema_AddWebappUrl.migration + V91_UpdateDomainRegistrationSchema_AddWebappUrl.migration, + V92_AddUserType.migration -- FUTUREWORK: undo V41 (searchable flag); we stopped using it in -- https://github.com/wireapp/wire-server/pull/964 ] diff --git a/services/brig/src/Brig/Schema/V92_AddUserType.hs b/services/brig/src/Brig/Schema/V92_AddUserType.hs new file mode 100644 index 00000000000..da5b15bfb63 --- /dev/null +++ b/services/brig/src/Brig/Schema/V92_AddUserType.hs @@ -0,0 +1,36 @@ +{-# LANGUAGE QuasiQuotes #-} + +-- This file is part of the Wire Server implementation. +-- +-- Copyright (C) 2026 Wire Swiss GmbH +-- +-- This program is free software: you can redistribute it and/or modify it under +-- the terms of the GNU Affero General Public License as published by the Free +-- Software Foundation, either version 3 of the License, or (at your option) any +-- later version. +-- +-- This program is distributed in the hope that it will be useful, but WITHOUT +-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +-- details. +-- +-- You should have received a copy of the GNU Affero General Public License along +-- with this program. If not, see . + +module Brig.Schema.V92_AddUserType + ( migration, + ) +where + +import Cassandra.Schema +import Imports +import Text.RawString.QQ + +migration :: Migration +migration = + Migration 92 "add user_type column" $ do + schema' + [r| + ALTER TABLE user ADD + ( user_type int ) + |]