Skip to content

Commit

Permalink
Add feature flag "conferenceCalling" (#1683)
Browse files Browse the repository at this point in the history
* wire-api: Add TeamFeatureConferenceCalling

* galley-types: add default flag & permissions

* Add getFeatureStatusWithDefaultConfig

* setFeatureStatusNoConfig: Add push event hook

* Add migration for feature flags

* implement galley endpoints

* internal endpoints and fix

* extend test

* add simple test

* correct a comment

* merge event testing into simple flag

* update CHANGELOG

* update schema

* update galley.integration.yaml for doc

* galley-types: fix unit test

* Add docs to config options

* fix typo
  • Loading branch information
smatting authored Jul 29, 2021
1 parent da93dcf commit 31479d9
Show file tree
Hide file tree
Showing 19 changed files with 154 additions and 64 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Upgrade nginz (#1658)

* Extend feature config API (#1658)
* `fileSharing` feature config (#1652, #1654, #1655)
* `conferenceCalling` feature flag (#1683)
* Add user_id to csv export (#1663)
* Validate server TLS certificate between federators (#1662)

Expand Down
1 change: 1 addition & 0 deletions docs/reference/cassandra-schema.cql
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ CREATE TABLE galley_test.team_features (
app_lock_enforce int,
app_lock_inactivity_timeout_secs int,
app_lock_status int,
conference_calling int,
digital_signatures int,
file_sharing int,
legalhold_status int,
Expand Down
17 changes: 17 additions & 0 deletions docs/reference/config-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,23 @@ classifiedDomains:
domains: []
```

### Conference Calling

The `conferenceCalling` feature flag controls whether a user can initiate a conference call. The flag can be toggled between its states `enabled` and `disabled` per team via an internal endpoint.

The `conferenceCalling` section in `featureFlags` defines the state of the `conferenceCalling` feature flag for all personal users (users that don't belong to a team). For personal users there is no way to toggle the flag, so the setting of the config section wholly defines the state of `conferenceCalling` flag for all personal users.

The `conferenceCalling` section in `featureFlags` also defines the _initial_ state of the `conferenceCalling` flag for all teams. After the flag is set for the first time for a team via the internal endpoint the value from the config section will be ignored.

Example value for the config section:
```yaml
conferenceCalling:
defaults:
status: enabled
```

The `conferenceCalling` section is optional in `featureFlags`. If it is omitted then it is assumed to be `enabled`.

### Federation Domain

Regardless of whether a backend wants to enable federation or not, the operator
Expand Down
11 changes: 8 additions & 3 deletions libs/galley-types/src/Galley/Types/Teams.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module Galley.Types.Teams
flagFileSharing,
flagAppLockDefaults,
flagClassifiedDomains,
flagConferenceCalling,
Defaults (..),
unDefaults,
FeatureSSO (..),
Expand Down Expand Up @@ -199,7 +200,8 @@ data FeatureFlags = FeatureFlags
_flagTeamSearchVisibility :: !FeatureTeamSearchVisibility,
_flagAppLockDefaults :: !(Defaults (TeamFeatureStatus 'TeamFeatureAppLock)),
_flagClassifiedDomains :: !(TeamFeatureStatus 'TeamFeatureClassifiedDomains),
_flagFileSharing :: !(Defaults (TeamFeatureStatus 'TeamFeatureFileSharing))
_flagFileSharing :: !(Defaults (TeamFeatureStatus 'TeamFeatureFileSharing)),
_flagConferenceCalling :: !(Defaults (TeamFeatureStatus 'TeamFeatureConferenceCalling))
}
deriving (Eq, Show, Generic)

Expand Down Expand Up @@ -244,16 +246,18 @@ instance FromJSON FeatureFlags where
<*> (fromMaybe (Defaults defaultAppLockStatus) <$> (obj .:? "appLock"))
<*> (fromMaybe defaultClassifiedDomains <$> (obj .:? "classifiedDomains"))
<*> (fromMaybe (Defaults (TeamFeatureStatusNoConfig TeamFeatureEnabled)) <$> (obj .:? "fileSharing"))
<*> (fromMaybe (Defaults (TeamFeatureStatusNoConfig TeamFeatureEnabled)) <$> (obj .:? "conferenceCalling"))

instance ToJSON FeatureFlags where
toJSON (FeatureFlags sso legalhold searchVisibility appLock classifiedDomains fileSharing) =
toJSON (FeatureFlags sso legalhold searchVisibility appLock classifiedDomains fileSharing conferenceCalling) =
object $
[ "sso" .= sso,
"legalhold" .= legalhold,
"teamSearchVisibility" .= searchVisibility,
"appLock" .= appLock,
"classifiedDomains" .= classifiedDomains,
"fileSharing" .= fileSharing
"fileSharing" .= fileSharing,
"conferenceCalling" .= conferenceCalling
]

instance FromJSON FeatureSSO where
Expand Down Expand Up @@ -363,6 +367,7 @@ roleHiddenPermissions role = HiddenPermissions p p
ViewTeamFeature TeamFeatureAppLock,
ViewTeamFeature TeamFeatureFileSharing,
ViewTeamFeature TeamFeatureClassifiedDomains,
ViewTeamFeature TeamFeatureConferenceCalling,
ViewLegalHoldUserSettings,
ViewTeamSearchVisibility
]
Expand Down
1 change: 1 addition & 0 deletions libs/galley-types/test/unit/Test/Galley/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,4 @@ instance Arbitrary FeatureFlags where
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary
1 change: 1 addition & 0 deletions libs/wire-api/src/Wire/API/Event/FeatureConfig.hs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ taggedEventDataSchema =
TeamFeatureAppLock -> tag _EdFeatureApplockChanged (unnamed schema)
TeamFeatureFileSharing -> tag _EdFeatureWithoutConfigChanged (unnamed schema)
TeamFeatureClassifiedDomains -> tag _EdFeatureClassifiedDomainsChanged (unnamed schema)
TeamFeatureConferenceCalling -> tag _EdFeatureWithoutConfigChanged (unnamed schema)

eventObjectSchema :: ObjectSchema SwaggerDoc Event
eventObjectSchema =
Expand Down
8 changes: 7 additions & 1 deletion libs/wire-api/src/Wire/API/Routes/Public/Galley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,9 @@ data Api routes = Api
teamFeatureStatusClassifiedDomainsGet ::
routes
:- FeatureStatusGet 'TeamFeatureClassifiedDomains,
teamFeatureStatusConferenceCallingGet ::
routes
:- FeatureStatusGet 'TeamFeatureConferenceCalling,
featureAllFeatureConfigsGet ::
routes
:- AllFeatureConfigsGet,
Expand All @@ -398,7 +401,10 @@ data Api routes = Api
:- FeatureConfigGet 'TeamFeatureFileSharing,
featureConfigClassifiedDomainsGet ::
routes
:- FeatureConfigGet 'TeamFeatureClassifiedDomains
:- FeatureConfigGet 'TeamFeatureClassifiedDomains,
featureConfigConferenceCallingGet ::
routes
:- FeatureConfigGet 'TeamFeatureConferenceCalling
}
deriving (Generic)

Expand Down
16 changes: 14 additions & 2 deletions libs/wire-api/src/Wire/API/Team/Feature.hs
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,14 @@ import Wire.API.Arbitrary (Arbitrary, GenericUniform (..))
--
-- * libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs
-- * add call to 'testRoundTrip'
-- * services/galley/src/Galley/API/Internal.hs
-- * add a field to the 'InternalApi routes' record)
-- * libs/wire-api/src/Wire/API/Routes/Public/Galley.hs
-- * add a GET (and possible PUT) route with name prefix teamFeature<FEATURE_NAME>
-- * add a GET route with name prefix featureConfig<FEATURE_NAME>
-- * services/galley/src/Galley/API/Internal.hs
-- * add a field to the 'InternalApi routes' record)
-- * libs/galley-types/src/Galley/Types/Teams.hs
-- * FeatureFlags for server config file
-- * roleHiddenPermissions ChangeTeamFeature and ViewTeamFeature
-- * services/galley/src/Galley/API/Teams/Features.hs
-- * extend getAllFeatureConfigs
-- * extend getAllFeatures
Expand Down Expand Up @@ -114,6 +117,7 @@ data TeamFeatureName
| TeamFeatureAppLock
| TeamFeatureFileSharing
| TeamFeatureClassifiedDomains
| TeamFeatureConferenceCalling
deriving stock (Eq, Show, Ord, Generic, Enum, Bounded, Typeable)
deriving (Arbitrary) via (GenericUniform TeamFeatureName)

Expand Down Expand Up @@ -153,6 +157,10 @@ instance KnownTeamFeatureName 'TeamFeatureClassifiedDomains where
type KnownTeamFeatureNameSymbol 'TeamFeatureClassifiedDomains = "classifiedDomains"
knownTeamFeatureName = TeamFeatureClassifiedDomains

instance KnownTeamFeatureName 'TeamFeatureConferenceCalling where
type KnownTeamFeatureNameSymbol 'TeamFeatureConferenceCalling = "conferenceCalling"
knownTeamFeatureName = TeamFeatureConferenceCalling

instance FromByteString TeamFeatureName where
parser =
Parser.takeByteString >>= \b ->
Expand All @@ -169,6 +177,7 @@ instance FromByteString TeamFeatureName where
Right "appLock" -> pure TeamFeatureAppLock
Right "fileSharing" -> pure TeamFeatureFileSharing
Right "classifiedDomains" -> pure TeamFeatureClassifiedDomains
Right "conferenceCalling" -> pure TeamFeatureConferenceCalling
Right t -> fail $ "Invalid TeamFeatureName: " <> T.unpack t

-- TODO: how do we make this consistent with 'KnownTeamFeatureNameSymbol'? add a test for
Expand All @@ -182,6 +191,7 @@ instance ToByteString TeamFeatureName where
builder TeamFeatureAppLock = "appLock"
builder TeamFeatureFileSharing = "fileSharing"
builder TeamFeatureClassifiedDomains = "classifiedDomains"
builder TeamFeatureConferenceCalling = "conferenceCalling"

instance ToSchema TeamFeatureName where
schema =
Expand Down Expand Up @@ -256,6 +266,7 @@ type family TeamFeatureStatus (a :: TeamFeatureName) :: * where
TeamFeatureStatus 'TeamFeatureAppLock = TeamFeatureStatusWithConfig TeamFeatureAppLockConfig
TeamFeatureStatus 'TeamFeatureFileSharing = TeamFeatureStatusNoConfig
TeamFeatureStatus 'TeamFeatureClassifiedDomains = TeamFeatureStatusWithConfig TeamFeatureClassifiedDomainsConfig
TeamFeatureStatus 'TeamFeatureConferenceCalling = TeamFeatureStatusNoConfig

type FeatureHasNoConfig (a :: TeamFeatureName) = (TeamFeatureStatus a ~ TeamFeatureStatusNoConfig) :: Constraint

Expand All @@ -269,6 +280,7 @@ modelForTeamFeature TeamFeatureDigitalSignatures = modelTeamFeatureStatusNoConfi
modelForTeamFeature name@TeamFeatureAppLock = modelTeamFeatureStatusWithConfig name modelTeamFeatureAppLockConfig
modelForTeamFeature TeamFeatureFileSharing = modelTeamFeatureStatusNoConfig
modelForTeamFeature name@TeamFeatureClassifiedDomains = modelTeamFeatureStatusWithConfig name modelTeamFeatureClassifiedDomainsConfig
modelForTeamFeature TeamFeatureConferenceCalling = modelTeamFeatureStatusNoConfig

----------------------------------------------------------------------
-- TeamFeatureStatusNoConfig
Expand Down
1 change: 1 addition & 0 deletions libs/wire-api/test/unit/Test/Wire/API/Roundtrip/Aeson.hs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ tests =
testRoundTrip @(Team.Feature.TeamFeatureStatus 'Team.Feature.TeamFeatureAppLock),
testRoundTrip @(Team.Feature.TeamFeatureStatus 'Team.Feature.TeamFeatureFileSharing),
testRoundTrip @(Team.Feature.TeamFeatureStatus 'Team.Feature.TeamFeatureClassifiedDomains),
testRoundTrip @(Team.Feature.TeamFeatureStatus 'Team.Feature.TeamFeatureConferenceCalling),
testRoundTrip @Team.Feature.TeamFeatureStatusValue,
testRoundTrip @Team.Invitation.InvitationRequest,
testRoundTrip @Team.Invitation.Invitation,
Expand Down
3 changes: 2 additions & 1 deletion services/galley/galley.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ cabal-version: 1.12
--
-- see: https://github.com/sol/hpack
--
-- hash: bb7dc649f5fc67c5e449f5b5984e8d7ae01caa5833ba0fa6a5db4f45cd5b1b42
-- hash: 8728b07d9aff8cd747ea5ca33259f9168c966ae28ca76825e0a073e166d43213

name: galley
version: 0.83.0
Expand Down Expand Up @@ -357,6 +357,7 @@ executable galley-schema
V49_ReAddRemoteIdentifiers
V50_AddLegalholdWhitelisted
V51_FeatureFileSharing
V52_FeatureConferenceCalling
Paths_galley
hs-source-dirs:
schema/src
Expand Down
3 changes: 3 additions & 0 deletions services/galley/galley.integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ settings:
fileSharing:
defaults:
status: enabled
conferenceCalling:
defaults:
status: enabled

logLevel: Info
logNetStrings: false
Expand Down
4 changes: 3 additions & 1 deletion services/galley/schema/src/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import qualified V48_DeleteRemoteIdentifiers
import qualified V49_ReAddRemoteIdentifiers
import qualified V50_AddLegalholdWhitelisted
import qualified V51_FeatureFileSharing
import qualified V52_FeatureConferenceCalling

main :: IO ()
main = do
Expand Down Expand Up @@ -93,7 +94,8 @@ main = do
V48_DeleteRemoteIdentifiers.migration,
V49_ReAddRemoteIdentifiers.migration,
V50_AddLegalholdWhitelisted.migration,
V51_FeatureFileSharing.migration
V51_FeatureFileSharing.migration,
V52_FeatureConferenceCalling.migration
-- When adding migrations here, don't forget to update
-- 'schemaVersion' in Galley.Data
]
Expand Down
29 changes: 29 additions & 0 deletions services/galley/schema/src/V52_FeatureConferenceCalling.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2020 Wire Swiss GmbH <opensource@wire.com>
--
-- 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 <https://www.gnu.org/licenses/>.

module V52_FeatureConferenceCalling
( migration,
)
where

import Cassandra.Schema
import Imports
import Text.RawString.QQ

migration :: Migration
migration = Migration 52 "Add feature flag \"conferenceCalling\"." $ do
schema' [r| ALTER TABLE team_features ADD conference_calling int; |]
12 changes: 10 additions & 2 deletions services/galley/src/Galley/API/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,13 @@ data InternalApi routes = InternalApi
:- IFeatureStatusPut 'Public.TeamFeatureFileSharing,
iTeamFeatureStatusClassifiedDomainsGet ::
routes
:- IFeatureStatusGet 'Public.TeamFeatureClassifiedDomains
:- IFeatureStatusGet 'Public.TeamFeatureClassifiedDomains,
iTeamFeatureStatusConferenceCallingPut ::
routes
:- IFeatureStatusPut 'Public.TeamFeatureConferenceCalling,
iTeamFeatureStatusConferenceCallingGet ::
routes
:- IFeatureStatusGet 'Public.TeamFeatureConferenceCalling
}
deriving (Generic)

Expand Down Expand Up @@ -222,7 +228,9 @@ servantSitemap =
iTeamFeatureStatusAppLockPut = iPutTeamFeature @'Public.TeamFeatureAppLock Features.setAppLockInternal,
iTeamFeatureStatusFileSharingGet = iGetTeamFeature @'Public.TeamFeatureFileSharing Features.getFileSharingInternal,
iTeamFeatureStatusFileSharingPut = iPutTeamFeature @'Public.TeamFeatureFileSharing Features.setFileSharingInternal,
iTeamFeatureStatusClassifiedDomainsGet = iGetTeamFeature @'Public.TeamFeatureClassifiedDomains Features.getClassifiedDomainsInternal
iTeamFeatureStatusClassifiedDomainsGet = iGetTeamFeature @'Public.TeamFeatureClassifiedDomains Features.getClassifiedDomainsInternal,
iTeamFeatureStatusConferenceCallingPut = iPutTeamFeature @'Public.TeamFeatureConferenceCalling Features.setConferenceCallingInternal,
iTeamFeatureStatusConferenceCallingGet = iGetTeamFeature @'Public.TeamFeatureConferenceCalling Features.getConferenceCallingInternal
}

iGetTeamFeature ::
Expand Down
6 changes: 5 additions & 1 deletion services/galley/src/Galley/API/Public.hs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ servantSitemap =
GalleyAPI.teamFeatureStatusClassifiedDomainsGet =
getFeatureStatus @'Public.TeamFeatureClassifiedDomains Features.getClassifiedDomainsInternal
. DoAuth,
GalleyAPI.teamFeatureStatusConferenceCallingGet =
getFeatureStatus @'Public.TeamFeatureConferenceCalling Features.getConferenceCallingInternal
. DoAuth,
GalleyAPI.featureAllFeatureConfigsGet = Features.getAllFeatureConfigs,
GalleyAPI.featureConfigLegalHoldGet = Features.getFeatureConfig @'Public.TeamFeatureLegalHold Features.getLegalholdStatusInternal,
GalleyAPI.featureConfigSSOGet = Features.getFeatureConfig @'Public.TeamFeatureSSO Features.getSSOStatusInternal,
Expand All @@ -147,7 +150,8 @@ servantSitemap =
GalleyAPI.featureConfigDigitalSignaturesGet = Features.getFeatureConfig @'Public.TeamFeatureDigitalSignatures Features.getDigitalSignaturesInternal,
GalleyAPI.featureConfigAppLockGet = Features.getFeatureConfig @'Public.TeamFeatureAppLock Features.getAppLockInternal,
GalleyAPI.featureConfigFileSharingGet = Features.getFeatureConfig @'Public.TeamFeatureFileSharing Features.getFileSharingInternal,
GalleyAPI.featureConfigClassifiedDomainsGet = Features.getFeatureConfig @'Public.TeamFeatureClassifiedDomains Features.getClassifiedDomainsInternal
GalleyAPI.featureConfigClassifiedDomainsGet = Features.getFeatureConfig @'Public.TeamFeatureClassifiedDomains Features.getClassifiedDomainsInternal,
GalleyAPI.featureConfigConferenceCallingGet = Features.getFeatureConfig @'Public.TeamFeatureConferenceCalling Features.getConferenceCallingInternal
}

sitemap :: Routes ApiBuilder Galley ()
Expand Down
Loading

0 comments on commit 31479d9

Please sign in to comment.