Skip to content

Commit 260601e

Browse files
committed
Added TTL to endpoint responses on feature status.
1 parent 5c04a2b commit 260601e

File tree

27 files changed

+247
-169
lines changed

27 files changed

+247
-169
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ default: fast
3838
init:
3939
mkdir -p dist
4040

41+
.PHONY: generate-cabal-from-hpack
42+
generate-cabal-from-hpack:
43+
stack build --dry-run
44+
4145
# Build all Haskell services and executables, run unit tests
4246
.PHONY: install
4347
install: init
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added TTL data to stern feature flag GET endpoint.

charts/galley/values.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,16 @@ config:
3838
enforceAppLock: false
3939
inactivityTimeoutSecs: 60
4040
status: enabled
41+
ttl: unlimited
4142
classifiedDomains:
4243
config:
4344
domains: []
4445
status: disabled
46+
ttl: unlimited
4547
conferenceCalling:
4648
defaults:
4749
status: enabled
50+
ttl: unlimited
4851
conversationGuestLinks:
4952
defaults:
5053
lockStatus: unlocked
@@ -53,6 +56,7 @@ config:
5356
defaults:
5457
lockStatus: unlocked
5558
status: enabled
59+
ttl: unlimited
5660
legalhold: disabled-by-default
5761
mls:
5862
defaults:

libs/types-common/src/Util/Options.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,11 @@ getOptions desc pars defaultPath = do
129129
(True, _) -> do
130130
configFile <- decodeFileEither path
131131
case configFile of
132-
Left e -> fail $ show e
132+
Left e ->
133+
fail $
134+
show e
135+
<> " while attempting to decode "
136+
<> show path
133137
Right o -> pure o
134138
-- Config doesn't exist but at least we have a CLI options parser
135139
(False, Just p) -> do

libs/wire-api/package.yaml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,20 @@ library:
6666
- mtl
6767
- pem >=0.2
6868
- polysemy
69-
- protobuf >=0.2
7069
- proto-lens
70+
- protobuf >=0.2
7171
- QuickCheck >=2.14
7272
- quickcheck-instances >=0.3.16
7373
- random >=1.2.0
7474
- resourcet
75+
- schema-profunctor
76+
- scientific
7577
- servant-client
7678
- servant-client-core
7779
- servant-conduit
7880
- servant-multipart
7981
- servant-server
8082
- servant-swagger
81-
- schema-profunctor
8283
- singletons
8384
- sop-core
8485
- string-conversions
@@ -92,13 +93,13 @@ library:
9293
- utf8-string
9394
- uuid >=1.3
9495
- vector >= 0.12
95-
- wire-message-proto-lens
96-
- x509
9796
- wai
9897
- wai-extra
9998
- wai-utilities
10099
- wai-websockets
101100
- websockets
101+
- wire-message-proto-lens
102+
- x509
102103

103104
tests:
104105
wire-api-tests:

libs/wire-api/src/Wire/API/Team/Feature.hs

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ where
7979
import qualified Cassandra.CQL as Cass
8080
import Control.Lens (makeLenses, (?~))
8181
import qualified Data.Aeson as A
82+
import qualified Data.Aeson.Types as A
8283
import qualified Data.Attoparsec.ByteString as Parser
8384
import Data.ByteString.Conversion
8485
import qualified Data.ByteString.UTF8 as UTF8
@@ -87,6 +88,7 @@ import Data.Either.Extra (maybeToEither)
8788
import Data.Id
8889
import Data.Proxy
8990
import Data.Schema
91+
import Data.Scientific (toBoundedInteger)
9092
import Data.String.Conversions (cs)
9193
import qualified Data.Swagger as S
9294
import qualified Data.Swagger.Build.Api as Doc
@@ -174,7 +176,8 @@ featureNameBS = UTF8.fromString $ symbolVal (Proxy @(FeatureSymbol cfg))
174176
data WithStatusBase (m :: * -> *) (cfg :: *) = WithStatusBase
175177
{ wsbStatus :: m FeatureStatus,
176178
wsbLockStatus :: m LockStatus,
177-
wsbConfig :: m cfg
179+
wsbConfig :: m cfg,
180+
wsbTTL :: m FeatureTTL
178181
}
179182
deriving stock (Generic, Typeable, Functor)
180183

@@ -191,17 +194,20 @@ wsLockStatus = runIdentity . wsbLockStatus
191194
wsConfig :: WithStatus cfg -> cfg
192195
wsConfig = runIdentity . wsbConfig
193196

194-
withStatus :: FeatureStatus -> LockStatus -> cfg -> WithStatus cfg
195-
withStatus s ls c = WithStatusBase (Identity s) (Identity ls) (Identity c)
197+
wsTTL :: WithStatus cfg -> FeatureTTL
198+
wsTTL = runIdentity . wsbTTL
199+
200+
withStatus :: FeatureStatus -> LockStatus -> cfg -> FeatureTTL -> WithStatus cfg
201+
withStatus s ls c ttl = WithStatusBase (Identity s) (Identity ls) (Identity c) (Identity ttl)
196202

197203
setStatus :: FeatureStatus -> WithStatus cfg -> WithStatus cfg
198-
setStatus s (WithStatusBase _ ls c) = WithStatusBase (Identity s) ls c
204+
setStatus s (WithStatusBase _ ls c ttl) = WithStatusBase (Identity s) ls c ttl
199205

200206
setLockStatus :: LockStatus -> WithStatus cfg -> WithStatus cfg
201-
setLockStatus ls (WithStatusBase s _ c) = WithStatusBase s (Identity ls) c
207+
setLockStatus ls (WithStatusBase s _ c ttl) = WithStatusBase s (Identity ls) c ttl
202208

203209
setConfig :: cfg -> WithStatus cfg -> WithStatus cfg
204-
setConfig c (WithStatusBase s ls _) = WithStatusBase s ls (Identity c)
210+
setConfig c (WithStatusBase s ls _ ttl) = WithStatusBase s ls (Identity c) ttl
205211

206212
type WithStatus (cfg :: *) = WithStatusBase Identity cfg
207213

@@ -222,12 +228,13 @@ instance (ToSchema cfg, IsFeatureConfig cfg) => ToSchema (WithStatus cfg) where
222228
<$> (runIdentity . wsbStatus) .= (Identity <$> field "status" schema)
223229
<*> (runIdentity . wsbLockStatus) .= (Identity <$> field "lockStatus" schema)
224230
<*> (runIdentity . wsbConfig) .= (Identity <$> objectSchema @cfg)
231+
<*> (runIdentity . wsbTTL) .= (Identity <$> field "ttl" schema)
225232
where
226233
inner = schema @cfg
227234
name = fromMaybe "" (getName (schemaDoc inner)) <> ".WithStatus"
228235

229236
instance (Arbitrary cfg, IsFeatureConfig cfg) => Arbitrary (WithStatus cfg) where
230-
arbitrary = WithStatusBase <$> arbitrary <*> arbitrary <*> arbitrary
237+
arbitrary = WithStatusBase <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
231238

232239
withStatusModel :: forall cfg. (IsFeatureConfig cfg, KnownSymbol (FeatureSymbol cfg)) => Doc.Model
233240
withStatusModel =
@@ -267,7 +274,7 @@ wspLockStatus = wsbLockStatus
267274
wspConfig :: WithStatusPatch cfg -> Maybe cfg
268275
wspConfig = wsbConfig
269276

270-
withStatus' :: Maybe FeatureStatus -> Maybe LockStatus -> Maybe cfg -> WithStatusPatch cfg
277+
withStatus' :: Maybe FeatureStatus -> Maybe LockStatus -> Maybe cfg -> Maybe FeatureTTL -> WithStatusPatch cfg
271278
withStatus' = WithStatusBase
272279

273280
-- | The ToJSON implementation of `WithStatusPatch` will encode the trivial config as `"config": {}`
@@ -279,31 +286,33 @@ instance (ToSchema cfg, IsFeatureConfig cfg) => ToSchema (WithStatusPatch cfg) w
279286
<$> wsbStatus .= maybe_ (optField "status" schema)
280287
<*> wsbLockStatus .= maybe_ (optField "lockStatus" schema)
281288
<*> wsbConfig .= maybe_ (optField "config" schema)
289+
<*> wsbTTL .= maybe_ (optField "ttl" schema)
282290
where
283291
inner = schema @cfg
284292
name = fromMaybe "" (getName (schemaDoc inner)) <> ".WithStatusPatch"
285293

286294
instance (Arbitrary cfg, IsFeatureConfig cfg) => Arbitrary (WithStatusPatch cfg) where
287-
arbitrary = WithStatusBase <$> arbitrary <*> arbitrary <*> arbitrary
295+
arbitrary = WithStatusBase <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
288296

289297
----------------------------------------------------------------------
290298
-- WithStatusNoLock
291299

292300
data WithStatusNoLock (cfg :: *) = WithStatusNoLock
293301
{ wssStatus :: FeatureStatus,
294-
wssConfig :: cfg
302+
wssConfig :: cfg,
303+
wssTTL :: FeatureTTL
295304
}
296305
deriving stock (Eq, Show, Generic, Typeable, Functor)
297306
deriving (ToJSON, FromJSON, S.ToSchema) via (Schema (WithStatusNoLock cfg))
298307

299308
instance Arbitrary cfg => Arbitrary (WithStatusNoLock cfg) where
300-
arbitrary = WithStatusNoLock <$> arbitrary <*> arbitrary
309+
arbitrary = WithStatusNoLock <$> arbitrary <*> arbitrary <*> arbitrary
301310

302311
forgetLock :: WithStatus a -> WithStatusNoLock a
303-
forgetLock ws = WithStatusNoLock (wsStatus ws) (wsConfig ws)
312+
forgetLock ws = WithStatusNoLock (wsStatus ws) (wsConfig ws) (wsTTL ws)
304313

305314
withLockStatus :: LockStatus -> WithStatusNoLock a -> WithStatus a
306-
withLockStatus ls (WithStatusNoLock s c) = withStatus s ls c
315+
withLockStatus ls (WithStatusNoLock s c ttl) = withStatus s ls c ttl
307316

308317
withUnlocked :: WithStatusNoLock a -> WithStatus a
309318
withUnlocked = withLockStatus LockStatusUnlocked
@@ -317,6 +326,7 @@ instance (ToSchema cfg, IsFeatureConfig cfg) => ToSchema (WithStatusNoLock cfg)
317326
WithStatusNoLock
318327
<$> wssStatus .= field "status" schema
319328
<*> wssConfig .= objectSchema @cfg
329+
<*> wssTTL .= field "ttl" schema
320330
where
321331
inner = schema @cfg
322332
name = fromMaybe "" (getName (schemaDoc inner)) <> ".WithStatusNoLock"
@@ -346,6 +356,35 @@ data FeatureTTL
346356
deriving stock (Eq, Show, Generic)
347357
deriving (Arbitrary) via (GenericUniform FeatureTTL)
348358

359+
instance ToSchema FeatureTTL where
360+
schema = mkSchema ttlDoc toTTL fromTTL
361+
where
362+
ttlDoc :: NamedSwaggerDoc
363+
ttlDoc = swaggerDoc @Word & S.schema . S.example ?~ "unlimited"
364+
365+
toTTL :: A.Value -> A.Parser FeatureTTL
366+
toTTL v = parseUnlimited v <|> parseSeconds v
367+
368+
parseUnlimited :: A.Value -> A.Parser FeatureTTL
369+
parseUnlimited =
370+
A.withText "FeatureTTL" $
371+
\t ->
372+
if t == "unlimited" || t == "0"
373+
then pure FeatureTTLUnlimited
374+
else A.parseFail "Expected ''unlimited' or '0'."
375+
376+
parseSeconds :: A.Value -> A.Parser FeatureTTL
377+
parseSeconds = A.withScientific "FeatureTTL" $
378+
\s -> case toBoundedInteger s of
379+
Just 0 -> pure FeatureTTLUnlimited
380+
Just i -> pure . FeatureTTLSeconds $ i
381+
Nothing -> A.parseFail "Expected an integer."
382+
383+
fromTTL :: FeatureTTL -> Maybe A.Value
384+
fromTTL FeatureTTLUnlimited = Just "unlimited"
385+
fromTTL (FeatureTTLSeconds 0) = Just "unlimited"
386+
fromTTL (FeatureTTLSeconds s) = A.decode . toByteString $ s
387+
349388
instance ToHttpApiData FeatureTTL where
350389
toQueryParam = T.decodeUtf8 . toByteString'
351390

@@ -532,7 +571,7 @@ instance ToSchema GuestLinksConfig where
532571

533572
instance IsFeatureConfig GuestLinksConfig where
534573
type FeatureSymbol GuestLinksConfig = "conversationGuestLinks"
535-
defFeatureStatus = withStatus FeatureStatusEnabled LockStatusUnlocked GuestLinksConfig
574+
defFeatureStatus = withStatus FeatureStatusEnabled LockStatusUnlocked GuestLinksConfig FeatureTTLUnlimited
536575

537576
objectSchema = pure GuestLinksConfig
538577

@@ -548,8 +587,7 @@ data LegalholdConfig = LegalholdConfig
548587

549588
instance IsFeatureConfig LegalholdConfig where
550589
type FeatureSymbol LegalholdConfig = "legalhold"
551-
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked LegalholdConfig
552-
590+
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked LegalholdConfig FeatureTTLUnlimited
553591
objectSchema = pure LegalholdConfig
554592

555593
instance ToSchema LegalholdConfig where
@@ -567,8 +605,7 @@ data SSOConfig = SSOConfig
567605

568606
instance IsFeatureConfig SSOConfig where
569607
type FeatureSymbol SSOConfig = "sso"
570-
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked SSOConfig
571-
608+
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked SSOConfig FeatureTTLUnlimited
572609
objectSchema = pure SSOConfig
573610

574611
instance ToSchema SSOConfig where
@@ -588,8 +625,7 @@ data SearchVisibilityAvailableConfig = SearchVisibilityAvailableConfig
588625

589626
instance IsFeatureConfig SearchVisibilityAvailableConfig where
590627
type FeatureSymbol SearchVisibilityAvailableConfig = "searchVisibility"
591-
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked SearchVisibilityAvailableConfig
592-
628+
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked SearchVisibilityAvailableConfig FeatureTTLUnlimited
593629
objectSchema = pure SearchVisibilityAvailableConfig
594630

595631
instance ToSchema SearchVisibilityAvailableConfig where
@@ -613,8 +649,7 @@ instance ToSchema ValidateSAMLEmailsConfig where
613649

614650
instance IsFeatureConfig ValidateSAMLEmailsConfig where
615651
type FeatureSymbol ValidateSAMLEmailsConfig = "validateSAMLemails"
616-
defFeatureStatus = withStatus FeatureStatusEnabled LockStatusUnlocked ValidateSAMLEmailsConfig
617-
652+
defFeatureStatus = withStatus FeatureStatusEnabled LockStatusUnlocked ValidateSAMLEmailsConfig FeatureTTLUnlimited
618653
objectSchema = pure ValidateSAMLEmailsConfig
619654

620655
instance HasDeprecatedFeatureName ValidateSAMLEmailsConfig where
@@ -632,8 +667,7 @@ data DigitalSignaturesConfig = DigitalSignaturesConfig
632667

633668
instance IsFeatureConfig DigitalSignaturesConfig where
634669
type FeatureSymbol DigitalSignaturesConfig = "digitalSignatures"
635-
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked DigitalSignaturesConfig
636-
670+
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked DigitalSignaturesConfig FeatureTTLUnlimited
637671
objectSchema = pure DigitalSignaturesConfig
638672

639673
instance HasDeprecatedFeatureName DigitalSignaturesConfig where
@@ -654,8 +688,7 @@ data ConferenceCallingConfig = ConferenceCallingConfig
654688

655689
instance IsFeatureConfig ConferenceCallingConfig where
656690
type FeatureSymbol ConferenceCallingConfig = "conferenceCalling"
657-
defFeatureStatus = withStatus FeatureStatusEnabled LockStatusUnlocked ConferenceCallingConfig
658-
691+
defFeatureStatus = withStatus FeatureStatusEnabled LockStatusUnlocked ConferenceCallingConfig FeatureTTLUnlimited
659692
objectSchema = pure ConferenceCallingConfig
660693

661694
instance ToSchema ConferenceCallingConfig where
@@ -676,8 +709,7 @@ instance ToSchema SndFactorPasswordChallengeConfig where
676709

677710
instance IsFeatureConfig SndFactorPasswordChallengeConfig where
678711
type FeatureSymbol SndFactorPasswordChallengeConfig = "sndFactorPasswordChallenge"
679-
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusLocked SndFactorPasswordChallengeConfig
680-
712+
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusLocked SndFactorPasswordChallengeConfig FeatureTTLUnlimited
681713
objectSchema = pure SndFactorPasswordChallengeConfig
682714

683715
instance FeatureTrivialConfig SndFactorPasswordChallengeConfig where
@@ -692,8 +724,7 @@ data SearchVisibilityInboundConfig = SearchVisibilityInboundConfig
692724

693725
instance IsFeatureConfig SearchVisibilityInboundConfig where
694726
type FeatureSymbol SearchVisibilityInboundConfig = "searchVisibilityInbound"
695-
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked SearchVisibilityInboundConfig
696-
727+
defFeatureStatus = withStatus FeatureStatusDisabled LockStatusUnlocked SearchVisibilityInboundConfig FeatureTTLUnlimited
697728
objectSchema = pure SearchVisibilityInboundConfig
698729

699730
instance ToSchema SearchVisibilityInboundConfig where
@@ -727,6 +758,7 @@ instance IsFeatureConfig ClassifiedDomainsConfig where
727758
FeatureStatusDisabled
728759
LockStatusUnlocked
729760
(ClassifiedDomainsConfig [])
761+
FeatureTTLUnlimited
730762
configModel = Just $
731763
Doc.defineModel "ClassifiedDomainsConfig" $ do
732764
Doc.property "domains" (Doc.array Doc.string') $ Doc.description "domains"
@@ -758,6 +790,7 @@ instance IsFeatureConfig AppLockConfig where
758790
FeatureStatusEnabled
759791
LockStatusUnlocked
760792
(AppLockConfig (EnforceAppLock False) 60)
793+
FeatureTTLUnlimited
761794
configModel = Just $
762795
Doc.defineModel "AppLockConfig" $ do
763796
Doc.property "enforceAppLock" Doc.bool' $ Doc.description "enforceAppLock"
@@ -781,8 +814,7 @@ data FileSharingConfig = FileSharingConfig
781814

782815
instance IsFeatureConfig FileSharingConfig where
783816
type FeatureSymbol FileSharingConfig = "fileSharing"
784-
defFeatureStatus = withStatus FeatureStatusEnabled LockStatusUnlocked FileSharingConfig
785-
817+
defFeatureStatus = withStatus FeatureStatusEnabled LockStatusUnlocked FileSharingConfig FeatureTTLUnlimited
786818
objectSchema = pure FileSharingConfig
787819

788820
instance ToSchema FileSharingConfig where
@@ -814,7 +846,7 @@ instance IsFeatureConfig SelfDeletingMessagesConfig where
814846
FeatureStatusEnabled
815847
LockStatusUnlocked
816848
(SelfDeletingMessagesConfig 0)
817-
849+
FeatureTTLUnlimited
818850
configModel = Just $
819851
Doc.defineModel "SelfDeletingMessagesConfig" $ do
820852
Doc.property "enforcedTimeoutSeconds" Doc.int32' $ Doc.description "optional; default: `0` (no enforcement)"
@@ -845,7 +877,7 @@ instance IsFeatureConfig MLSConfig where
845877
type FeatureSymbol MLSConfig = "mls"
846878
defFeatureStatus =
847879
let config = MLSConfig [] ProtocolProteusTag [MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519] MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
848-
in withStatus FeatureStatusDisabled LockStatusUnlocked config
880+
in withStatus FeatureStatusDisabled LockStatusUnlocked config FeatureTTLUnlimited
849881
objectSchema = field "config" schema
850882

851883
configModel = Just $

0 commit comments

Comments
 (0)