Skip to content

Commit 79fa65a

Browse files
committed
Added TTL to endpoint responses on feature status.
1 parent 9b48232 commit 79fa65a

File tree

21 files changed

+194
-131
lines changed

21 files changed

+194
-131
lines changed

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: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ where
6767
import qualified Cassandra.CQL as Cass
6868
import Control.Lens (makeLenses, (?~))
6969
import qualified Data.Aeson as A
70+
import qualified Data.Aeson.Types as A
7071
import qualified Data.Attoparsec.ByteString as Parser
7172
import Data.ByteString.Conversion
7273
import qualified Data.ByteString.UTF8 as UTF8
@@ -75,6 +76,7 @@ import Data.Either.Extra (maybeToEither)
7576
import Data.Id
7677
import Data.Proxy
7778
import Data.Schema
79+
import Data.Scientific (toBoundedInteger)
7880
import Data.String.Conversions (cs)
7981
import qualified Data.Swagger as S
8082
import qualified Data.Swagger.Build.Api as Doc
@@ -162,13 +164,14 @@ featureNameBS = UTF8.fromString $ symbolVal (Proxy @(FeatureSymbol cfg))
162164
data WithStatus (cfg :: *) = WithStatus
163165
{ wsStatus :: FeatureStatus,
164166
wsLockStatus :: LockStatus,
165-
wsConfig :: cfg
167+
wsConfig :: cfg,
168+
wsTTL :: FeatureTTL
166169
}
167170
deriving stock (Eq, Show, Generic, Typeable, Functor)
168171
deriving (ToJSON, FromJSON, S.ToSchema) via (Schema (WithStatus cfg))
169172

170173
instance Arbitrary cfg => Arbitrary (WithStatus cfg) where
171-
arbitrary = WithStatus <$> arbitrary <*> arbitrary <*> arbitrary
174+
arbitrary = WithStatus <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
172175

173176
instance (ToSchema cfg, IsFeatureConfig cfg) => ToSchema (WithStatus cfg) where
174177
schema =
@@ -177,6 +180,7 @@ instance (ToSchema cfg, IsFeatureConfig cfg) => ToSchema (WithStatus cfg) where
177180
<$> wsStatus .= field "status" schema
178181
<*> wsLockStatus .= field "lockStatus" schema
179182
<*> wsConfig .= objectSchema @cfg
183+
<*> wsTTL .= field "ttl" schema
180184
where
181185
inner = schema @cfg
182186
name = fromMaybe "" (getName (schemaDoc inner)) <> ".WithStatus"
@@ -200,19 +204,20 @@ withStatusModel =
200204

201205
data WithStatusNoLock (cfg :: *) = WithStatusNoLock
202206
{ wssStatus :: FeatureStatus,
203-
wssConfig :: cfg
207+
wssConfig :: cfg,
208+
wssTTL :: FeatureTTL
204209
}
205210
deriving stock (Eq, Show, Generic, Typeable, Functor)
206211
deriving (ToJSON, FromJSON, S.ToSchema) via (Schema (WithStatusNoLock cfg))
207212

208213
instance Arbitrary cfg => Arbitrary (WithStatusNoLock cfg) where
209-
arbitrary = WithStatusNoLock <$> arbitrary <*> arbitrary
214+
arbitrary = WithStatusNoLock <$> arbitrary <*> arbitrary <*> arbitrary
210215

211216
forgetLock :: WithStatus a -> WithStatusNoLock a
212-
forgetLock WithStatus {..} = WithStatusNoLock wsStatus wsConfig
217+
forgetLock WithStatus {..} = WithStatusNoLock wsStatus wsConfig wsTTL
213218

214219
withLockStatus :: LockStatus -> WithStatusNoLock a -> WithStatus a
215-
withLockStatus ls (WithStatusNoLock s c) = WithStatus s ls c
220+
withLockStatus ls (WithStatusNoLock s c ttl) = WithStatus s ls c ttl
216221

217222
withUnlocked :: WithStatusNoLock a -> WithStatus a
218223
withUnlocked = withLockStatus LockStatusUnlocked
@@ -226,6 +231,7 @@ instance (ToSchema cfg, IsFeatureConfig cfg) => ToSchema (WithStatusNoLock cfg)
226231
WithStatusNoLock
227232
<$> wssStatus .= field "status" schema
228233
<*> wssConfig .= objectSchema @cfg
234+
<*> wssTTL .= field "ttl" schema
229235
where
230236
inner = schema @cfg
231237
name = fromMaybe "" (getName (schemaDoc inner)) <> ".WithStatusNoLock"
@@ -255,6 +261,35 @@ data FeatureTTL
255261
deriving stock (Eq, Show, Generic)
256262
deriving (Arbitrary) via (GenericUniform FeatureTTL)
257263

264+
instance ToSchema FeatureTTL where
265+
schema = mkSchema ttlDoc toTTL fromTTL
266+
where
267+
ttlDoc :: NamedSwaggerDoc
268+
ttlDoc = swaggerDoc @Word & S.schema . S.example ?~ "unlimited"
269+
270+
toTTL :: A.Value -> A.Parser FeatureTTL
271+
toTTL v = parseUnlimited v <|> parseSeconds v
272+
273+
parseUnlimited :: A.Value -> A.Parser FeatureTTL
274+
parseUnlimited =
275+
A.withText "FeatureTTL" $
276+
\t ->
277+
if t == "unlimited" || t == "0"
278+
then pure FeatureTTLUnlimited
279+
else A.parseFail "Expected ''unlimited' or '0'."
280+
281+
parseSeconds :: A.Value -> A.Parser FeatureTTL
282+
parseSeconds = A.withScientific "FeatureTTL" $
283+
\s -> case toBoundedInteger s of
284+
Just 0 -> pure FeatureTTLUnlimited
285+
Just i -> pure . FeatureTTLSeconds $ i
286+
Nothing -> A.parseFail "Expected an integer."
287+
288+
fromTTL :: FeatureTTL -> Maybe A.Value
289+
fromTTL FeatureTTLUnlimited = Just "unlimited"
290+
fromTTL (FeatureTTLSeconds 0) = Just "unlimited"
291+
fromTTL (FeatureTTLSeconds s) = A.decode . toByteString $ s
292+
258293
instance ToHttpApiData FeatureTTL where
259294
toQueryParam = T.decodeUtf8 . toByteString'
260295

@@ -441,7 +476,7 @@ instance ToSchema GuestLinksConfig where
441476

442477
instance IsFeatureConfig GuestLinksConfig where
443478
type FeatureSymbol GuestLinksConfig = "conversationGuestLinks"
444-
defFeatureStatus = WithStatus FeatureStatusEnabled LockStatusUnlocked GuestLinksConfig
479+
defFeatureStatus = WithStatus FeatureStatusEnabled LockStatusUnlocked GuestLinksConfig FeatureTTLUnlimited
445480
objectSchema = pure GuestLinksConfig
446481

447482
instance FeatureTrivialConfig GuestLinksConfig where
@@ -456,7 +491,7 @@ data LegalholdConfig = LegalholdConfig
456491

457492
instance IsFeatureConfig LegalholdConfig where
458493
type FeatureSymbol LegalholdConfig = "legalhold"
459-
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusUnlocked LegalholdConfig
494+
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusUnlocked LegalholdConfig FeatureTTLUnlimited
460495
objectSchema = pure LegalholdConfig
461496

462497
instance ToSchema LegalholdConfig where
@@ -474,7 +509,7 @@ data SSOConfig = SSOConfig
474509

475510
instance IsFeatureConfig SSOConfig where
476511
type FeatureSymbol SSOConfig = "sso"
477-
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusUnlocked SSOConfig
512+
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusUnlocked SSOConfig FeatureTTLUnlimited
478513
objectSchema = pure SSOConfig
479514

480515
instance ToSchema SSOConfig where
@@ -494,7 +529,7 @@ data SearchVisibilityAvailableConfig = SearchVisibilityAvailableConfig
494529

495530
instance IsFeatureConfig SearchVisibilityAvailableConfig where
496531
type FeatureSymbol SearchVisibilityAvailableConfig = "searchVisibility"
497-
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusUnlocked SearchVisibilityAvailableConfig
532+
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusUnlocked SearchVisibilityAvailableConfig FeatureTTLUnlimited
498533
objectSchema = pure SearchVisibilityAvailableConfig
499534

500535
instance ToSchema SearchVisibilityAvailableConfig where
@@ -518,7 +553,7 @@ instance ToSchema ValidateSAMLEmailsConfig where
518553

519554
instance IsFeatureConfig ValidateSAMLEmailsConfig where
520555
type FeatureSymbol ValidateSAMLEmailsConfig = "validateSAMLemails"
521-
defFeatureStatus = WithStatus FeatureStatusEnabled LockStatusUnlocked ValidateSAMLEmailsConfig
556+
defFeatureStatus = WithStatus FeatureStatusEnabled LockStatusUnlocked ValidateSAMLEmailsConfig FeatureTTLUnlimited
522557
objectSchema = pure ValidateSAMLEmailsConfig
523558

524559
instance HasDeprecatedFeatureName ValidateSAMLEmailsConfig where
@@ -536,7 +571,7 @@ data DigitalSignaturesConfig = DigitalSignaturesConfig
536571

537572
instance IsFeatureConfig DigitalSignaturesConfig where
538573
type FeatureSymbol DigitalSignaturesConfig = "digitalSignatures"
539-
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusUnlocked DigitalSignaturesConfig
574+
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusUnlocked DigitalSignaturesConfig FeatureTTLUnlimited
540575
objectSchema = pure DigitalSignaturesConfig
541576

542577
instance HasDeprecatedFeatureName DigitalSignaturesConfig where
@@ -557,7 +592,7 @@ data ConferenceCallingConfig = ConferenceCallingConfig
557592

558593
instance IsFeatureConfig ConferenceCallingConfig where
559594
type FeatureSymbol ConferenceCallingConfig = "conferenceCalling"
560-
defFeatureStatus = WithStatus FeatureStatusEnabled LockStatusUnlocked ConferenceCallingConfig
595+
defFeatureStatus = WithStatus FeatureStatusEnabled LockStatusUnlocked ConferenceCallingConfig FeatureTTLUnlimited
561596
objectSchema = pure ConferenceCallingConfig
562597

563598
instance ToSchema ConferenceCallingConfig where
@@ -578,7 +613,7 @@ instance ToSchema SndFactorPasswordChallengeConfig where
578613

579614
instance IsFeatureConfig SndFactorPasswordChallengeConfig where
580615
type FeatureSymbol SndFactorPasswordChallengeConfig = "sndFactorPasswordChallenge"
581-
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusLocked SndFactorPasswordChallengeConfig
616+
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusLocked SndFactorPasswordChallengeConfig FeatureTTLUnlimited
582617
objectSchema = pure SndFactorPasswordChallengeConfig
583618

584619
instance FeatureTrivialConfig SndFactorPasswordChallengeConfig where
@@ -593,7 +628,7 @@ data SearchVisibilityInboundConfig = SearchVisibilityInboundConfig
593628

594629
instance IsFeatureConfig SearchVisibilityInboundConfig where
595630
type FeatureSymbol SearchVisibilityInboundConfig = "searchVisibilityInbound"
596-
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusUnlocked SearchVisibilityInboundConfig
631+
defFeatureStatus = WithStatus FeatureStatusDisabled LockStatusUnlocked SearchVisibilityInboundConfig FeatureTTLUnlimited
597632
objectSchema = pure SearchVisibilityInboundConfig
598633

599634
instance ToSchema SearchVisibilityInboundConfig where
@@ -626,6 +661,7 @@ instance IsFeatureConfig ClassifiedDomainsConfig where
626661
FeatureStatusDisabled
627662
LockStatusUnlocked
628663
(ClassifiedDomainsConfig [])
664+
FeatureTTLUnlimited
629665
configModel = Just $
630666
Doc.defineModel "ClassifiedDomainsConfig" $ do
631667
Doc.property "domains" (Doc.array Doc.string') $ Doc.description "domains"
@@ -656,6 +692,7 @@ instance IsFeatureConfig AppLockConfig where
656692
FeatureStatusEnabled
657693
LockStatusUnlocked
658694
(AppLockConfig (EnforceAppLock False) 60)
695+
FeatureTTLUnlimited
659696
configModel = Just $
660697
Doc.defineModel "AppLockConfig" $ do
661698
Doc.property "enforceAppLock" Doc.bool' $ Doc.description "enforceAppLock"
@@ -679,7 +716,7 @@ data FileSharingConfig = FileSharingConfig
679716

680717
instance IsFeatureConfig FileSharingConfig where
681718
type FeatureSymbol FileSharingConfig = "fileSharing"
682-
defFeatureStatus = WithStatus FeatureStatusEnabled LockStatusUnlocked FileSharingConfig
719+
defFeatureStatus = WithStatus FeatureStatusEnabled LockStatusUnlocked FileSharingConfig FeatureTTLUnlimited
683720
objectSchema = pure FileSharingConfig
684721

685722
instance ToSchema FileSharingConfig where
@@ -711,6 +748,7 @@ instance IsFeatureConfig SelfDeletingMessagesConfig where
711748
FeatureStatusEnabled
712749
LockStatusUnlocked
713750
(SelfDeletingMessagesConfig 0)
751+
FeatureTTLUnlimited
714752
configModel = Just $
715753
Doc.defineModel "SelfDeletingMessagesConfig" $ do
716754
Doc.property "enforcedTimeoutSeconds" Doc.int32' $ Doc.description "optional; default: `0` (no enforcement)"
@@ -741,7 +779,7 @@ instance IsFeatureConfig MLSConfig where
741779
type FeatureSymbol MLSConfig = "mls"
742780
defFeatureStatus =
743781
let config = MLSConfig [] ProtocolProteusTag [MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519] MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
744-
in WithStatus FeatureStatusDisabled LockStatusUnlocked config
782+
in WithStatus FeatureStatusDisabled LockStatusUnlocked config FeatureTTLUnlimited
745783
objectSchema = field "config" schema
746784
configModel = Just $
747785
Doc.defineModel "MLSConfig" $ do

libs/wire-api/test/golden/Test/Wire/API/Golden/Generated/WithStatusNoLock_team.hs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,52 +24,52 @@ import Imports
2424
import Wire.API.Team.Feature
2525

2626
testObject_WithStatusNoLock_team_1 :: WithStatusNoLock AppLockConfig
27-
testObject_WithStatusNoLock_team_1 = WithStatusNoLock FeatureStatusEnabled (AppLockConfig (EnforceAppLock False) (-98))
27+
testObject_WithStatusNoLock_team_1 = WithStatusNoLock FeatureStatusEnabled (AppLockConfig (EnforceAppLock False) (-98)) FeatureTTLUnlimited
2828

2929
testObject_WithStatusNoLock_team_2 :: WithStatusNoLock AppLockConfig
30-
testObject_WithStatusNoLock_team_2 = WithStatusNoLock FeatureStatusEnabled (AppLockConfig (EnforceAppLock True) 0)
30+
testObject_WithStatusNoLock_team_2 = WithStatusNoLock FeatureStatusEnabled (AppLockConfig (EnforceAppLock True) 0) FeatureTTLUnlimited
3131

3232
testObject_WithStatusNoLock_team_3 :: WithStatusNoLock AppLockConfig
33-
testObject_WithStatusNoLock_team_3 = WithStatusNoLock FeatureStatusEnabled (AppLockConfig (EnforceAppLock True) 111)
33+
testObject_WithStatusNoLock_team_3 = WithStatusNoLock FeatureStatusEnabled (AppLockConfig (EnforceAppLock True) 111) FeatureTTLUnlimited
3434

3535
testObject_WithStatusNoLock_team_4 :: WithStatusNoLock SelfDeletingMessagesConfig
36-
testObject_WithStatusNoLock_team_4 = WithStatusNoLock FeatureStatusEnabled (SelfDeletingMessagesConfig (-97))
36+
testObject_WithStatusNoLock_team_4 = WithStatusNoLock FeatureStatusEnabled (SelfDeletingMessagesConfig (-97)) FeatureTTLUnlimited
3737

3838
testObject_WithStatusNoLock_team_5 :: WithStatusNoLock SelfDeletingMessagesConfig
39-
testObject_WithStatusNoLock_team_5 = WithStatusNoLock FeatureStatusEnabled (SelfDeletingMessagesConfig 0)
39+
testObject_WithStatusNoLock_team_5 = WithStatusNoLock FeatureStatusEnabled (SelfDeletingMessagesConfig 0) FeatureTTLUnlimited
4040

4141
testObject_WithStatusNoLock_team_6 :: WithStatusNoLock SelfDeletingMessagesConfig
42-
testObject_WithStatusNoLock_team_6 = WithStatusNoLock FeatureStatusEnabled (SelfDeletingMessagesConfig 77)
42+
testObject_WithStatusNoLock_team_6 = WithStatusNoLock FeatureStatusEnabled (SelfDeletingMessagesConfig 77) FeatureTTLUnlimited
4343

4444
testObject_WithStatusNoLock_team_7 :: WithStatusNoLock ClassifiedDomainsConfig
45-
testObject_WithStatusNoLock_team_7 = WithStatusNoLock FeatureStatusEnabled (ClassifiedDomainsConfig [])
45+
testObject_WithStatusNoLock_team_7 = WithStatusNoLock FeatureStatusEnabled (ClassifiedDomainsConfig []) FeatureTTLUnlimited
4646

4747
testObject_WithStatusNoLock_team_8 :: WithStatusNoLock ClassifiedDomainsConfig
48-
testObject_WithStatusNoLock_team_8 = WithStatusNoLock FeatureStatusEnabled (ClassifiedDomainsConfig [Domain "example.com", Domain "test.foobar"])
48+
testObject_WithStatusNoLock_team_8 = WithStatusNoLock FeatureStatusEnabled (ClassifiedDomainsConfig [Domain "example.com", Domain "test.foobar"]) FeatureTTLUnlimited
4949

5050
testObject_WithStatusNoLock_team_9 :: WithStatusNoLock ClassifiedDomainsConfig
51-
testObject_WithStatusNoLock_team_9 = WithStatusNoLock FeatureStatusEnabled (ClassifiedDomainsConfig [Domain "test.foobar"])
51+
testObject_WithStatusNoLock_team_9 = WithStatusNoLock FeatureStatusEnabled (ClassifiedDomainsConfig [Domain "test.foobar"]) FeatureTTLUnlimited
5252

5353
testObject_WithStatusNoLock_team_10 :: WithStatusNoLock SSOConfig
54-
testObject_WithStatusNoLock_team_10 = WithStatusNoLock FeatureStatusDisabled SSOConfig
54+
testObject_WithStatusNoLock_team_10 = WithStatusNoLock FeatureStatusDisabled SSOConfig FeatureTTLUnlimited
5555

5656
testObject_WithStatusNoLock_team_11 :: WithStatusNoLock SearchVisibilityAvailableConfig
57-
testObject_WithStatusNoLock_team_11 = WithStatusNoLock FeatureStatusEnabled SearchVisibilityAvailableConfig
57+
testObject_WithStatusNoLock_team_11 = WithStatusNoLock FeatureStatusEnabled SearchVisibilityAvailableConfig FeatureTTLUnlimited
5858

5959
testObject_WithStatusNoLock_team_12 :: WithStatusNoLock ValidateSAMLEmailsConfig
60-
testObject_WithStatusNoLock_team_12 = WithStatusNoLock FeatureStatusDisabled ValidateSAMLEmailsConfig
60+
testObject_WithStatusNoLock_team_12 = WithStatusNoLock FeatureStatusDisabled ValidateSAMLEmailsConfig FeatureTTLUnlimited
6161

6262
testObject_WithStatusNoLock_team_13 :: WithStatusNoLock DigitalSignaturesConfig
63-
testObject_WithStatusNoLock_team_13 = WithStatusNoLock FeatureStatusEnabled DigitalSignaturesConfig
63+
testObject_WithStatusNoLock_team_13 = WithStatusNoLock FeatureStatusEnabled DigitalSignaturesConfig FeatureTTLUnlimited
6464

6565
testObject_WithStatusNoLock_team_14 :: WithStatusNoLock ConferenceCallingConfig
66-
testObject_WithStatusNoLock_team_14 = WithStatusNoLock FeatureStatusDisabled ConferenceCallingConfig
66+
testObject_WithStatusNoLock_team_14 = WithStatusNoLock FeatureStatusDisabled ConferenceCallingConfig FeatureTTLUnlimited
6767

6868
testObject_WithStatusNoLock_team_15 :: WithStatusNoLock GuestLinksConfig
69-
testObject_WithStatusNoLock_team_15 = WithStatusNoLock FeatureStatusEnabled GuestLinksConfig
69+
testObject_WithStatusNoLock_team_15 = WithStatusNoLock FeatureStatusEnabled GuestLinksConfig FeatureTTLUnlimited
7070

7171
testObject_WithStatusNoLock_team_16 :: WithStatusNoLock SndFactorPasswordChallengeConfig
72-
testObject_WithStatusNoLock_team_16 = WithStatusNoLock FeatureStatusDisabled SndFactorPasswordChallengeConfig
72+
testObject_WithStatusNoLock_team_16 = WithStatusNoLock FeatureStatusDisabled SndFactorPasswordChallengeConfig FeatureTTLUnlimited
7373

7474
testObject_WithStatusNoLock_team_17 :: WithStatusNoLock SearchVisibilityInboundConfig
75-
testObject_WithStatusNoLock_team_17 = WithStatusNoLock FeatureStatusEnabled SearchVisibilityInboundConfig
75+
testObject_WithStatusNoLock_team_17 = WithStatusNoLock FeatureStatusEnabled SearchVisibilityInboundConfig FeatureTTLUnlimited

0 commit comments

Comments
 (0)