diff --git a/Cargo.lock b/Cargo.lock index 49eb9f3d6..d1c0f2f3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1683,7 +1683,6 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" name = "context_aware_config" version = "0.101.0" dependencies = [ - "actix-http", "actix-web", "anyhow", "bigdecimal", @@ -2384,6 +2383,7 @@ dependencies = [ "superposition_derives", "superposition_macros", "superposition_types", + "tokio", ] [[package]] diff --git a/clients/haskell/sdk/Io/Superposition/Command/GetConfigFast.hs b/clients/haskell/sdk/Io/Superposition/Command/GetExperimentConfig.hs similarity index 63% rename from clients/haskell/sdk/Io/Superposition/Command/GetConfigFast.hs rename to clients/haskell/sdk/Io/Superposition/Command/GetExperimentConfig.hs index ec478a98a..30a7aafe7 100644 --- a/clients/haskell/sdk/Io/Superposition/Command/GetConfigFast.hs +++ b/clients/haskell/sdk/Io/Superposition/Command/GetExperimentConfig.hs @@ -1,27 +1,27 @@ -module Io.Superposition.Command.GetConfigFast ( - GetConfigFastError (..), - getConfigFast +module Io.Superposition.Command.GetExperimentConfig ( + GetExperimentConfigError (..), + getExperimentConfig ) where import qualified Data.Aeson import qualified Data.Maybe import qualified Data.Text import qualified GHC.Generics import qualified GHC.Show -import qualified Io.Superposition.Model.GetConfigFastInput -import qualified Io.Superposition.Model.GetConfigFastOutput +import qualified Io.Superposition.Model.GetExperimentConfigInput +import qualified Io.Superposition.Model.GetExperimentConfigOutput import qualified Io.Superposition.Model.InternalServerError import qualified Io.Superposition.SuperpositionClient import qualified Io.Superposition.Utility -data GetConfigFastError = +data GetExperimentConfigError = InternalServerError Io.Superposition.Model.InternalServerError.InternalServerError | BuilderError Data.Text.Text | DeSerializationError Io.Superposition.Utility.HttpMetadata Data.Text.Text | UnexpectedError (Data.Maybe.Maybe Io.Superposition.Utility.HttpMetadata) Data.Text.Text deriving (GHC.Generics.Generic, GHC.Show.Show) -instance Data.Aeson.ToJSON GetConfigFastError -instance Io.Superposition.Utility.OperationError GetConfigFastError where +instance Data.Aeson.ToJSON GetExperimentConfigError +instance Io.Superposition.Utility.OperationError GetExperimentConfigError where mkBuilderError = BuilderError mkDeSerializationError = DeSerializationError mkUnexpectedError = UnexpectedError @@ -31,10 +31,10 @@ instance Io.Superposition.Utility.OperationError GetConfigFastError where | otherwise = Nothing -getConfigFast :: Io.Superposition.SuperpositionClient.SuperpositionClient -> Io.Superposition.Model.GetConfigFastInput.GetConfigFastInputBuilder () -> IO (Either GetConfigFastError Io.Superposition.Model.GetConfigFastOutput.GetConfigFastOutput) -getConfigFast client builder = +getExperimentConfig :: Io.Superposition.SuperpositionClient.SuperpositionClient -> Io.Superposition.Model.GetExperimentConfigInput.GetExperimentConfigInputBuilder () -> IO (Either GetExperimentConfigError Io.Superposition.Model.GetExperimentConfigOutput.GetExperimentConfigOutput) +getExperimentConfig client builder = let endpoint = Io.Superposition.SuperpositionClient.endpointUri client manager = Io.Superposition.SuperpositionClient.httpManager client auth = Io.Superposition.SuperpositionClient.getAuth client - in Io.Superposition.Utility.runOperation endpoint manager auth (Io.Superposition.Model.GetConfigFastInput.build builder) + in Io.Superposition.Utility.runOperation endpoint manager auth (Io.Superposition.Model.GetExperimentConfigInput.build builder) diff --git a/clients/haskell/sdk/Io/Superposition/Model/GetConfigFastInput.hs b/clients/haskell/sdk/Io/Superposition/Model/GetConfigFastInput.hs deleted file mode 100644 index 57c659af8..000000000 --- a/clients/haskell/sdk/Io/Superposition/Model/GetConfigFastInput.hs +++ /dev/null @@ -1,94 +0,0 @@ -module Io.Superposition.Model.GetConfigFastInput ( - setWorkspaceId, - setOrgId, - build, - GetConfigFastInputBuilder, - GetConfigFastInput, - workspace_id, - org_id -) where -import qualified Control.Applicative -import qualified Control.Monad.State.Strict -import qualified Data.Aeson -import qualified Data.Either -import qualified Data.Eq -import qualified Data.Functor -import qualified Data.Maybe -import qualified Data.Text -import qualified GHC.Generics -import qualified GHC.Show -import qualified Io.Superposition.Utility -import qualified Network.HTTP.Types.Method - -data GetConfigFastInput = GetConfigFastInput { - workspace_id :: Data.Text.Text, - org_id :: Data.Text.Text -} deriving ( - GHC.Show.Show, - Data.Eq.Eq, - GHC.Generics.Generic - ) - -instance Data.Aeson.ToJSON GetConfigFastInput where - toJSON a = Data.Aeson.object [ - "workspace_id" Data.Aeson..= workspace_id a, - "org_id" Data.Aeson..= org_id a - ] - - -instance Io.Superposition.Utility.SerializeBody GetConfigFastInput - -instance Data.Aeson.FromJSON GetConfigFastInput where - parseJSON = Data.Aeson.withObject "GetConfigFastInput" $ \v -> GetConfigFastInput - Data.Functor.<$> (v Data.Aeson..: "workspace_id") - Control.Applicative.<*> (v Data.Aeson..: "org_id") - - - - -data GetConfigFastInputBuilderState = GetConfigFastInputBuilderState { - workspace_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, - org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text -} deriving ( - GHC.Generics.Generic - ) - -defaultBuilderState :: GetConfigFastInputBuilderState -defaultBuilderState = GetConfigFastInputBuilderState { - workspace_idBuilderState = Data.Maybe.Nothing, - org_idBuilderState = Data.Maybe.Nothing -} - -type GetConfigFastInputBuilder = Control.Monad.State.Strict.State GetConfigFastInputBuilderState - -setWorkspaceId :: Data.Text.Text -> GetConfigFastInputBuilder () -setWorkspaceId value = - Control.Monad.State.Strict.modify (\s -> (s { workspace_idBuilderState = Data.Maybe.Just value })) - -setOrgId :: Data.Text.Text -> GetConfigFastInputBuilder () -setOrgId value = - Control.Monad.State.Strict.modify (\s -> (s { org_idBuilderState = Data.Maybe.Just value })) - -build :: GetConfigFastInputBuilder () -> Data.Either.Either Data.Text.Text GetConfigFastInput -build builder = do - let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState - workspace_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetConfigFastInput.GetConfigFastInput.workspace_id is a required property.") Data.Either.Right (workspace_idBuilderState st) - org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetConfigFastInput.GetConfigFastInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) - Data.Either.Right (GetConfigFastInput { - workspace_id = workspace_id', - org_id = org_id' - }) - - -instance Io.Superposition.Utility.IntoRequestBuilder GetConfigFastInput where - intoRequestBuilder self = do - Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodGet - Io.Superposition.Utility.setPath [ - "config", - "fast" - ] - - Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) - Io.Superposition.Utility.serHeader "x-org-id" (org_id self) - - diff --git a/clients/haskell/sdk/Io/Superposition/Model/GetConfigFastOutput.hs b/clients/haskell/sdk/Io/Superposition/Model/GetConfigFastOutput.hs deleted file mode 100644 index 10b8e6b2d..000000000 --- a/clients/haskell/sdk/Io/Superposition/Model/GetConfigFastOutput.hs +++ /dev/null @@ -1,123 +0,0 @@ -module Io.Superposition.Model.GetConfigFastOutput ( - setConfig, - setVersion, - setLastModified, - setAuditId, - build, - GetConfigFastOutputBuilder, - GetConfigFastOutput, - config, - version, - last_modified, - audit_id -) where -import qualified Control.Applicative -import qualified Control.Monad.State.Strict -import qualified Data.Aeson -import qualified Data.Either -import qualified Data.Eq -import qualified Data.Functor -import qualified Data.Maybe -import qualified Data.Text -import qualified Data.Time -import qualified GHC.Generics -import qualified GHC.Show -import qualified Io.Superposition.Utility -import qualified Network.HTTP.Types - -data GetConfigFastOutput = GetConfigFastOutput { - config :: Data.Maybe.Maybe Data.Aeson.Value, - version :: Data.Maybe.Maybe Data.Text.Text, - last_modified :: Data.Maybe.Maybe Data.Time.UTCTime, - audit_id :: Data.Maybe.Maybe Data.Text.Text -} deriving ( - GHC.Show.Show, - Data.Eq.Eq, - GHC.Generics.Generic - ) - -instance Data.Aeson.ToJSON GetConfigFastOutput where - toJSON a = Data.Aeson.object [ - "config" Data.Aeson..= config a, - "version" Data.Aeson..= version a, - "last_modified" Data.Aeson..= last_modified a, - "audit_id" Data.Aeson..= audit_id a - ] - - -instance Io.Superposition.Utility.SerializeBody GetConfigFastOutput - -instance Data.Aeson.FromJSON GetConfigFastOutput where - parseJSON = Data.Aeson.withObject "GetConfigFastOutput" $ \v -> GetConfigFastOutput - Data.Functor.<$> (v Data.Aeson..:? "config") - Control.Applicative.<*> (v Data.Aeson..:? "version") - Control.Applicative.<*> (v Data.Aeson..:? "last_modified") - Control.Applicative.<*> (v Data.Aeson..:? "audit_id") - - - - -data GetConfigFastOutputBuilderState = GetConfigFastOutputBuilderState { - configBuilderState :: Data.Maybe.Maybe Data.Aeson.Value, - versionBuilderState :: Data.Maybe.Maybe Data.Text.Text, - last_modifiedBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime, - audit_idBuilderState :: Data.Maybe.Maybe Data.Text.Text -} deriving ( - GHC.Generics.Generic - ) - -defaultBuilderState :: GetConfigFastOutputBuilderState -defaultBuilderState = GetConfigFastOutputBuilderState { - configBuilderState = Data.Maybe.Nothing, - versionBuilderState = Data.Maybe.Nothing, - last_modifiedBuilderState = Data.Maybe.Nothing, - audit_idBuilderState = Data.Maybe.Nothing -} - -type GetConfigFastOutputBuilder = Control.Monad.State.Strict.State GetConfigFastOutputBuilderState - -setConfig :: Data.Maybe.Maybe Data.Aeson.Value -> GetConfigFastOutputBuilder () -setConfig value = - Control.Monad.State.Strict.modify (\s -> (s { configBuilderState = value })) - -setVersion :: Data.Maybe.Maybe Data.Text.Text -> GetConfigFastOutputBuilder () -setVersion value = - Control.Monad.State.Strict.modify (\s -> (s { versionBuilderState = value })) - -setLastModified :: Data.Maybe.Maybe Data.Time.UTCTime -> GetConfigFastOutputBuilder () -setLastModified value = - Control.Monad.State.Strict.modify (\s -> (s { last_modifiedBuilderState = value })) - -setAuditId :: Data.Maybe.Maybe Data.Text.Text -> GetConfigFastOutputBuilder () -setAuditId value = - Control.Monad.State.Strict.modify (\s -> (s { audit_idBuilderState = value })) - -build :: GetConfigFastOutputBuilder () -> Data.Either.Either Data.Text.Text GetConfigFastOutput -build builder = do - let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState - config' <- Data.Either.Right (configBuilderState st) - version' <- Data.Either.Right (versionBuilderState st) - last_modified' <- Data.Either.Right (last_modifiedBuilderState st) - audit_id' <- Data.Either.Right (audit_idBuilderState st) - Data.Either.Right (GetConfigFastOutput { - config = config', - version = version', - last_modified = last_modified', - audit_id = audit_id' - }) - - -instance Io.Superposition.Utility.FromResponseParser GetConfigFastOutput where - expectedStatus = Network.HTTP.Types.status200 - responseParser = do - var0 <- Io.Superposition.Utility.deSerHeader "x-audit-id" - var1 <- Io.Superposition.Utility.deSerHeader "x-config-version" - var2 <- Io.Superposition.Utility.deSerHeader "last-modified" - var3 <- Io.Superposition.Utility.deSerBody - pure $ GetConfigFastOutput { - config = var3, - version = var1, - last_modified = var2, - audit_id = var0 - } - diff --git a/clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs b/clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs index 0f4f447e8..c15f5230e 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/GetConfigInput.hs @@ -3,6 +3,7 @@ module Io.Superposition.Model.GetConfigInput ( setOrgId, setPrefix, setVersion, + setIfModifiedSince, setContext, build, GetConfigInputBuilder, @@ -11,6 +12,7 @@ module Io.Superposition.Model.GetConfigInput ( org_id, prefix, version, + if_modified_since, context ) where import qualified Control.Applicative @@ -22,6 +24,7 @@ import qualified Data.Functor import qualified Data.Map import qualified Data.Maybe import qualified Data.Text +import qualified Data.Time import qualified GHC.Generics import qualified GHC.Show import qualified Io.Superposition.Utility @@ -32,6 +35,7 @@ data GetConfigInput = GetConfigInput { org_id :: Data.Text.Text, prefix :: Data.Maybe.Maybe ([] Data.Text.Text), version :: Data.Maybe.Maybe Data.Text.Text, + if_modified_since :: Data.Maybe.Maybe Data.Time.UTCTime, context :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value) } deriving ( GHC.Show.Show, @@ -45,6 +49,7 @@ instance Data.Aeson.ToJSON GetConfigInput where "org_id" Data.Aeson..= org_id a, "prefix" Data.Aeson..= prefix a, "version" Data.Aeson..= version a, + "if_modified_since" Data.Aeson..= if_modified_since a, "context" Data.Aeson..= context a ] @@ -57,6 +62,7 @@ instance Data.Aeson.FromJSON GetConfigInput where Control.Applicative.<*> (v Data.Aeson..: "org_id") Control.Applicative.<*> (v Data.Aeson..:? "prefix") Control.Applicative.<*> (v Data.Aeson..:? "version") + Control.Applicative.<*> (v Data.Aeson..:? "if_modified_since") Control.Applicative.<*> (v Data.Aeson..:? "context") @@ -67,6 +73,7 @@ data GetConfigInputBuilderState = GetConfigInputBuilderState { org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, prefixBuilderState :: Data.Maybe.Maybe ([] Data.Text.Text), versionBuilderState :: Data.Maybe.Maybe Data.Text.Text, + if_modified_sinceBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime, contextBuilderState :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value) } deriving ( GHC.Generics.Generic @@ -78,6 +85,7 @@ defaultBuilderState = GetConfigInputBuilderState { org_idBuilderState = Data.Maybe.Nothing, prefixBuilderState = Data.Maybe.Nothing, versionBuilderState = Data.Maybe.Nothing, + if_modified_sinceBuilderState = Data.Maybe.Nothing, contextBuilderState = Data.Maybe.Nothing } @@ -99,6 +107,10 @@ setVersion :: Data.Maybe.Maybe Data.Text.Text -> GetConfigInputBuilder () setVersion value = Control.Monad.State.Strict.modify (\s -> (s { versionBuilderState = value })) +setIfModifiedSince :: Data.Maybe.Maybe Data.Time.UTCTime -> GetConfigInputBuilder () +setIfModifiedSince value = + Control.Monad.State.Strict.modify (\s -> (s { if_modified_sinceBuilderState = value })) + setContext :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value) -> GetConfigInputBuilder () setContext value = Control.Monad.State.Strict.modify (\s -> (s { contextBuilderState = value })) @@ -110,12 +122,14 @@ build builder = do org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetConfigInput.GetConfigInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) prefix' <- Data.Either.Right (prefixBuilderState st) version' <- Data.Either.Right (versionBuilderState st) + if_modified_since' <- Data.Either.Right (if_modified_sinceBuilderState st) context' <- Data.Either.Right (contextBuilderState st) Data.Either.Right (GetConfigInput { workspace_id = workspace_id', org_id = org_id', prefix = prefix', version = version', + if_modified_since = if_modified_since', context = context' }) @@ -129,6 +143,7 @@ instance Io.Superposition.Utility.IntoRequestBuilder GetConfigInput where Io.Superposition.Utility.serQuery "prefix" (prefix self) Io.Superposition.Utility.serQuery "version" (version self) Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) + Io.Superposition.Utility.serHeader "if-modified-since" (if_modified_since self) Io.Superposition.Utility.serHeader "x-org-id" (org_id self) Io.Superposition.Utility.serField "context" (context self) diff --git a/clients/haskell/sdk/Io/Superposition/Model/GetConfigJsonInput.hs b/clients/haskell/sdk/Io/Superposition/Model/GetConfigJsonInput.hs index 334072124..69d9ea314 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/GetConfigJsonInput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/GetConfigJsonInput.hs @@ -1,11 +1,13 @@ module Io.Superposition.Model.GetConfigJsonInput ( setWorkspaceId, setOrgId, + setIfModifiedSince, build, GetConfigJsonInputBuilder, GetConfigJsonInput, workspace_id, - org_id + org_id, + if_modified_since ) where import qualified Control.Applicative import qualified Control.Monad.State.Strict @@ -15,6 +17,7 @@ import qualified Data.Eq import qualified Data.Functor import qualified Data.Maybe import qualified Data.Text +import qualified Data.Time import qualified GHC.Generics import qualified GHC.Show import qualified Io.Superposition.Utility @@ -22,7 +25,8 @@ import qualified Network.HTTP.Types.Method data GetConfigJsonInput = GetConfigJsonInput { workspace_id :: Data.Text.Text, - org_id :: Data.Text.Text + org_id :: Data.Text.Text, + if_modified_since :: Data.Maybe.Maybe Data.Time.UTCTime } deriving ( GHC.Show.Show, Data.Eq.Eq, @@ -32,7 +36,8 @@ data GetConfigJsonInput = GetConfigJsonInput { instance Data.Aeson.ToJSON GetConfigJsonInput where toJSON a = Data.Aeson.object [ "workspace_id" Data.Aeson..= workspace_id a, - "org_id" Data.Aeson..= org_id a + "org_id" Data.Aeson..= org_id a, + "if_modified_since" Data.Aeson..= if_modified_since a ] @@ -42,13 +47,15 @@ instance Data.Aeson.FromJSON GetConfigJsonInput where parseJSON = Data.Aeson.withObject "GetConfigJsonInput" $ \v -> GetConfigJsonInput Data.Functor.<$> (v Data.Aeson..: "workspace_id") Control.Applicative.<*> (v Data.Aeson..: "org_id") + Control.Applicative.<*> (v Data.Aeson..:? "if_modified_since") data GetConfigJsonInputBuilderState = GetConfigJsonInputBuilderState { workspace_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, - org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text + org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, + if_modified_sinceBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime } deriving ( GHC.Generics.Generic ) @@ -56,7 +63,8 @@ data GetConfigJsonInputBuilderState = GetConfigJsonInputBuilderState { defaultBuilderState :: GetConfigJsonInputBuilderState defaultBuilderState = GetConfigJsonInputBuilderState { workspace_idBuilderState = Data.Maybe.Nothing, - org_idBuilderState = Data.Maybe.Nothing + org_idBuilderState = Data.Maybe.Nothing, + if_modified_sinceBuilderState = Data.Maybe.Nothing } type GetConfigJsonInputBuilder = Control.Monad.State.Strict.State GetConfigJsonInputBuilderState @@ -69,26 +77,33 @@ setOrgId :: Data.Text.Text -> GetConfigJsonInputBuilder () setOrgId value = Control.Monad.State.Strict.modify (\s -> (s { org_idBuilderState = Data.Maybe.Just value })) +setIfModifiedSince :: Data.Maybe.Maybe Data.Time.UTCTime -> GetConfigJsonInputBuilder () +setIfModifiedSince value = + Control.Monad.State.Strict.modify (\s -> (s { if_modified_sinceBuilderState = value })) + build :: GetConfigJsonInputBuilder () -> Data.Either.Either Data.Text.Text GetConfigJsonInput build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState workspace_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetConfigJsonInput.GetConfigJsonInput.workspace_id is a required property.") Data.Either.Right (workspace_idBuilderState st) org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetConfigJsonInput.GetConfigJsonInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) + if_modified_since' <- Data.Either.Right (if_modified_sinceBuilderState st) Data.Either.Right (GetConfigJsonInput { workspace_id = workspace_id', - org_id = org_id' + org_id = org_id', + if_modified_since = if_modified_since' }) instance Io.Superposition.Utility.IntoRequestBuilder GetConfigJsonInput where intoRequestBuilder self = do - Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodGet + Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodPost Io.Superposition.Utility.setPath [ "config", "json" ] Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) + Io.Superposition.Utility.serHeader "if-modified-since" (if_modified_since self) Io.Superposition.Utility.serHeader "x-org-id" (org_id self) diff --git a/clients/haskell/sdk/Io/Superposition/Model/GetConfigOutput.hs b/clients/haskell/sdk/Io/Superposition/Model/GetConfigOutput.hs index c549106e2..28eaf0b8f 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/GetConfigOutput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/GetConfigOutput.hs @@ -5,7 +5,6 @@ module Io.Superposition.Model.GetConfigOutput ( setDimensions, setVersion, setLastModified, - setAuditId, build, GetConfigOutputBuilder, GetConfigOutput, @@ -14,8 +13,7 @@ module Io.Superposition.Model.GetConfigOutput ( default_configs, dimensions, version, - last_modified, - audit_id + last_modified ) where import qualified Control.Applicative import qualified Control.Monad.State.Strict @@ -40,8 +38,7 @@ data GetConfigOutput = GetConfigOutput { default_configs :: Data.Map.Map Data.Text.Text Data.Aeson.Value, dimensions :: Data.Map.Map Data.Text.Text Io.Superposition.Model.DimensionInfo.DimensionInfo, version :: Data.Text.Text, - last_modified :: Data.Time.UTCTime, - audit_id :: Data.Maybe.Maybe Data.Text.Text + last_modified :: Data.Time.UTCTime } deriving ( GHC.Show.Show, Data.Eq.Eq, @@ -55,8 +52,7 @@ instance Data.Aeson.ToJSON GetConfigOutput where "default_configs" Data.Aeson..= default_configs a, "dimensions" Data.Aeson..= dimensions a, "version" Data.Aeson..= version a, - "last_modified" Data.Aeson..= last_modified a, - "audit_id" Data.Aeson..= audit_id a + "last_modified" Data.Aeson..= last_modified a ] @@ -70,7 +66,6 @@ instance Data.Aeson.FromJSON GetConfigOutput where Control.Applicative.<*> (v Data.Aeson..: "dimensions") Control.Applicative.<*> (v Data.Aeson..: "version") Control.Applicative.<*> (v Data.Aeson..: "last_modified") - Control.Applicative.<*> (v Data.Aeson..:? "audit_id") @@ -81,8 +76,7 @@ data GetConfigOutputBuilderState = GetConfigOutputBuilderState { default_configsBuilderState :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value), dimensionsBuilderState :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Io.Superposition.Model.DimensionInfo.DimensionInfo), versionBuilderState :: Data.Maybe.Maybe Data.Text.Text, - last_modifiedBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime, - audit_idBuilderState :: Data.Maybe.Maybe Data.Text.Text + last_modifiedBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime } deriving ( GHC.Generics.Generic ) @@ -94,8 +88,7 @@ defaultBuilderState = GetConfigOutputBuilderState { default_configsBuilderState = Data.Maybe.Nothing, dimensionsBuilderState = Data.Maybe.Nothing, versionBuilderState = Data.Maybe.Nothing, - last_modifiedBuilderState = Data.Maybe.Nothing, - audit_idBuilderState = Data.Maybe.Nothing + last_modifiedBuilderState = Data.Maybe.Nothing } type GetConfigOutputBuilder = Control.Monad.State.Strict.State GetConfigOutputBuilderState @@ -124,10 +117,6 @@ setLastModified :: Data.Time.UTCTime -> GetConfigOutputBuilder () setLastModified value = Control.Monad.State.Strict.modify (\s -> (s { last_modifiedBuilderState = Data.Maybe.Just value })) -setAuditId :: Data.Maybe.Maybe Data.Text.Text -> GetConfigOutputBuilder () -setAuditId value = - Control.Monad.State.Strict.modify (\s -> (s { audit_idBuilderState = value })) - build :: GetConfigOutputBuilder () -> Data.Either.Either Data.Text.Text GetConfigOutput build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState @@ -137,35 +126,31 @@ build builder = do dimensions' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetConfigOutput.GetConfigOutput.dimensions is a required property.") Data.Either.Right (dimensionsBuilderState st) version' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetConfigOutput.GetConfigOutput.version is a required property.") Data.Either.Right (versionBuilderState st) last_modified' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetConfigOutput.GetConfigOutput.last_modified is a required property.") Data.Either.Right (last_modifiedBuilderState st) - audit_id' <- Data.Either.Right (audit_idBuilderState st) Data.Either.Right (GetConfigOutput { contexts = contexts', overrides = overrides', default_configs = default_configs', dimensions = dimensions', version = version', - last_modified = last_modified', - audit_id = audit_id' + last_modified = last_modified' }) instance Io.Superposition.Utility.FromResponseParser GetConfigOutput where expectedStatus = Network.HTTP.Types.status200 responseParser = do - var0 <- Io.Superposition.Utility.deSerHeader "x-audit-id" - var1 <- Io.Superposition.Utility.deSerHeader "x-config-version" - var2 <- Io.Superposition.Utility.deSerHeader "last-modified" - var3 <- Io.Superposition.Utility.deSerField "contexts" - var4 <- Io.Superposition.Utility.deSerField "overrides" - var5 <- Io.Superposition.Utility.deSerField "default_configs" - var6 <- Io.Superposition.Utility.deSerField "dimensions" + var0 <- Io.Superposition.Utility.deSerHeader "x-config-version" + var1 <- Io.Superposition.Utility.deSerHeader "last-modified" + var2 <- Io.Superposition.Utility.deSerField "contexts" + var3 <- Io.Superposition.Utility.deSerField "overrides" + var4 <- Io.Superposition.Utility.deSerField "default_configs" + var5 <- Io.Superposition.Utility.deSerField "dimensions" pure $ GetConfigOutput { - contexts = var3, - overrides = var4, - default_configs = var5, - dimensions = var6, - version = var1, - last_modified = var2, - audit_id = var0 + contexts = var2, + overrides = var3, + default_configs = var4, + dimensions = var5, + version = var0, + last_modified = var1 } diff --git a/clients/haskell/sdk/Io/Superposition/Model/GetConfigTomlInput.hs b/clients/haskell/sdk/Io/Superposition/Model/GetConfigTomlInput.hs index d40811f03..e5e9f6246 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/GetConfigTomlInput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/GetConfigTomlInput.hs @@ -1,11 +1,13 @@ module Io.Superposition.Model.GetConfigTomlInput ( setWorkspaceId, setOrgId, + setIfModifiedSince, build, GetConfigTomlInputBuilder, GetConfigTomlInput, workspace_id, - org_id + org_id, + if_modified_since ) where import qualified Control.Applicative import qualified Control.Monad.State.Strict @@ -15,6 +17,7 @@ import qualified Data.Eq import qualified Data.Functor import qualified Data.Maybe import qualified Data.Text +import qualified Data.Time import qualified GHC.Generics import qualified GHC.Show import qualified Io.Superposition.Utility @@ -22,7 +25,8 @@ import qualified Network.HTTP.Types.Method data GetConfigTomlInput = GetConfigTomlInput { workspace_id :: Data.Text.Text, - org_id :: Data.Text.Text + org_id :: Data.Text.Text, + if_modified_since :: Data.Maybe.Maybe Data.Time.UTCTime } deriving ( GHC.Show.Show, Data.Eq.Eq, @@ -32,7 +36,8 @@ data GetConfigTomlInput = GetConfigTomlInput { instance Data.Aeson.ToJSON GetConfigTomlInput where toJSON a = Data.Aeson.object [ "workspace_id" Data.Aeson..= workspace_id a, - "org_id" Data.Aeson..= org_id a + "org_id" Data.Aeson..= org_id a, + "if_modified_since" Data.Aeson..= if_modified_since a ] @@ -42,13 +47,15 @@ instance Data.Aeson.FromJSON GetConfigTomlInput where parseJSON = Data.Aeson.withObject "GetConfigTomlInput" $ \v -> GetConfigTomlInput Data.Functor.<$> (v Data.Aeson..: "workspace_id") Control.Applicative.<*> (v Data.Aeson..: "org_id") + Control.Applicative.<*> (v Data.Aeson..:? "if_modified_since") data GetConfigTomlInputBuilderState = GetConfigTomlInputBuilderState { workspace_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, - org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text + org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, + if_modified_sinceBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime } deriving ( GHC.Generics.Generic ) @@ -56,7 +63,8 @@ data GetConfigTomlInputBuilderState = GetConfigTomlInputBuilderState { defaultBuilderState :: GetConfigTomlInputBuilderState defaultBuilderState = GetConfigTomlInputBuilderState { workspace_idBuilderState = Data.Maybe.Nothing, - org_idBuilderState = Data.Maybe.Nothing + org_idBuilderState = Data.Maybe.Nothing, + if_modified_sinceBuilderState = Data.Maybe.Nothing } type GetConfigTomlInputBuilder = Control.Monad.State.Strict.State GetConfigTomlInputBuilderState @@ -69,26 +77,33 @@ setOrgId :: Data.Text.Text -> GetConfigTomlInputBuilder () setOrgId value = Control.Monad.State.Strict.modify (\s -> (s { org_idBuilderState = Data.Maybe.Just value })) +setIfModifiedSince :: Data.Maybe.Maybe Data.Time.UTCTime -> GetConfigTomlInputBuilder () +setIfModifiedSince value = + Control.Monad.State.Strict.modify (\s -> (s { if_modified_sinceBuilderState = value })) + build :: GetConfigTomlInputBuilder () -> Data.Either.Either Data.Text.Text GetConfigTomlInput build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState workspace_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetConfigTomlInput.GetConfigTomlInput.workspace_id is a required property.") Data.Either.Right (workspace_idBuilderState st) org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetConfigTomlInput.GetConfigTomlInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) + if_modified_since' <- Data.Either.Right (if_modified_sinceBuilderState st) Data.Either.Right (GetConfigTomlInput { workspace_id = workspace_id', - org_id = org_id' + org_id = org_id', + if_modified_since = if_modified_since' }) instance Io.Superposition.Utility.IntoRequestBuilder GetConfigTomlInput where intoRequestBuilder self = do - Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodGet + Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodPost Io.Superposition.Utility.setPath [ "config", "toml" ] Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) + Io.Superposition.Utility.serHeader "if-modified-since" (if_modified_since self) Io.Superposition.Utility.serHeader "x-org-id" (org_id self) diff --git a/clients/haskell/sdk/Io/Superposition/Model/GetExperimentConfigInput.hs b/clients/haskell/sdk/Io/Superposition/Model/GetExperimentConfigInput.hs new file mode 100644 index 000000000..076065b1b --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/GetExperimentConfigInput.hs @@ -0,0 +1,150 @@ +module Io.Superposition.Model.GetExperimentConfigInput ( + setWorkspaceId, + setOrgId, + setIfModifiedSince, + setPrefix, + setContext, + setDimensionMatchStrategy, + build, + GetExperimentConfigInputBuilder, + GetExperimentConfigInput, + workspace_id, + org_id, + if_modified_since, + prefix, + context, + dimension_match_strategy +) where +import qualified Control.Applicative +import qualified Control.Monad.State.Strict +import qualified Data.Aeson +import qualified Data.Either +import qualified Data.Eq +import qualified Data.Functor +import qualified Data.Map +import qualified Data.Maybe +import qualified Data.Text +import qualified Data.Time +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Model.DimensionMatchStrategy +import qualified Io.Superposition.Utility +import qualified Network.HTTP.Types.Method + +data GetExperimentConfigInput = GetExperimentConfigInput { + workspace_id :: Data.Text.Text, + org_id :: Data.Text.Text, + if_modified_since :: Data.Maybe.Maybe Data.Time.UTCTime, + prefix :: Data.Maybe.Maybe ([] Data.Text.Text), + context :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value), + dimension_match_strategy :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMatchStrategy.DimensionMatchStrategy +} deriving ( + GHC.Show.Show, + Data.Eq.Eq, + GHC.Generics.Generic + ) + +instance Data.Aeson.ToJSON GetExperimentConfigInput where + toJSON a = Data.Aeson.object [ + "workspace_id" Data.Aeson..= workspace_id a, + "org_id" Data.Aeson..= org_id a, + "if_modified_since" Data.Aeson..= if_modified_since a, + "prefix" Data.Aeson..= prefix a, + "context" Data.Aeson..= context a, + "dimension_match_strategy" Data.Aeson..= dimension_match_strategy a + ] + + +instance Io.Superposition.Utility.SerializeBody GetExperimentConfigInput + +instance Data.Aeson.FromJSON GetExperimentConfigInput where + parseJSON = Data.Aeson.withObject "GetExperimentConfigInput" $ \v -> GetExperimentConfigInput + Data.Functor.<$> (v Data.Aeson..: "workspace_id") + Control.Applicative.<*> (v Data.Aeson..: "org_id") + Control.Applicative.<*> (v Data.Aeson..:? "if_modified_since") + Control.Applicative.<*> (v Data.Aeson..:? "prefix") + Control.Applicative.<*> (v Data.Aeson..:? "context") + Control.Applicative.<*> (v Data.Aeson..:? "dimension_match_strategy") + + + + +data GetExperimentConfigInputBuilderState = GetExperimentConfigInputBuilderState { + workspace_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, + org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, + if_modified_sinceBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime, + prefixBuilderState :: Data.Maybe.Maybe ([] Data.Text.Text), + contextBuilderState :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value), + dimension_match_strategyBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMatchStrategy.DimensionMatchStrategy +} deriving ( + GHC.Generics.Generic + ) + +defaultBuilderState :: GetExperimentConfigInputBuilderState +defaultBuilderState = GetExperimentConfigInputBuilderState { + workspace_idBuilderState = Data.Maybe.Nothing, + org_idBuilderState = Data.Maybe.Nothing, + if_modified_sinceBuilderState = Data.Maybe.Nothing, + prefixBuilderState = Data.Maybe.Nothing, + contextBuilderState = Data.Maybe.Nothing, + dimension_match_strategyBuilderState = Data.Maybe.Nothing +} + +type GetExperimentConfigInputBuilder = Control.Monad.State.Strict.State GetExperimentConfigInputBuilderState + +setWorkspaceId :: Data.Text.Text -> GetExperimentConfigInputBuilder () +setWorkspaceId value = + Control.Monad.State.Strict.modify (\s -> (s { workspace_idBuilderState = Data.Maybe.Just value })) + +setOrgId :: Data.Text.Text -> GetExperimentConfigInputBuilder () +setOrgId value = + Control.Monad.State.Strict.modify (\s -> (s { org_idBuilderState = Data.Maybe.Just value })) + +setIfModifiedSince :: Data.Maybe.Maybe Data.Time.UTCTime -> GetExperimentConfigInputBuilder () +setIfModifiedSince value = + Control.Monad.State.Strict.modify (\s -> (s { if_modified_sinceBuilderState = value })) + +setPrefix :: Data.Maybe.Maybe ([] Data.Text.Text) -> GetExperimentConfigInputBuilder () +setPrefix value = + Control.Monad.State.Strict.modify (\s -> (s { prefixBuilderState = value })) + +setContext :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value) -> GetExperimentConfigInputBuilder () +setContext value = + Control.Monad.State.Strict.modify (\s -> (s { contextBuilderState = value })) + +setDimensionMatchStrategy :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMatchStrategy.DimensionMatchStrategy -> GetExperimentConfigInputBuilder () +setDimensionMatchStrategy value = + Control.Monad.State.Strict.modify (\s -> (s { dimension_match_strategyBuilderState = value })) + +build :: GetExperimentConfigInputBuilder () -> Data.Either.Either Data.Text.Text GetExperimentConfigInput +build builder = do + let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState + workspace_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetExperimentConfigInput.GetExperimentConfigInput.workspace_id is a required property.") Data.Either.Right (workspace_idBuilderState st) + org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetExperimentConfigInput.GetExperimentConfigInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) + if_modified_since' <- Data.Either.Right (if_modified_sinceBuilderState st) + prefix' <- Data.Either.Right (prefixBuilderState st) + context' <- Data.Either.Right (contextBuilderState st) + dimension_match_strategy' <- Data.Either.Right (dimension_match_strategyBuilderState st) + Data.Either.Right (GetExperimentConfigInput { + workspace_id = workspace_id', + org_id = org_id', + if_modified_since = if_modified_since', + prefix = prefix', + context = context', + dimension_match_strategy = dimension_match_strategy' + }) + + +instance Io.Superposition.Utility.IntoRequestBuilder GetExperimentConfigInput where + intoRequestBuilder self = do + Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodPost + Io.Superposition.Utility.setPath [ + "experiment-config" + ] + Io.Superposition.Utility.serQuery "dimension_match_strategy" (dimension_match_strategy self) + Io.Superposition.Utility.serQuery "prefix" (prefix self) + Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) + Io.Superposition.Utility.serHeader "if-modified-since" (if_modified_since self) + Io.Superposition.Utility.serHeader "x-org-id" (org_id self) + Io.Superposition.Utility.serField "context" (context self) + diff --git a/clients/haskell/sdk/Io/Superposition/Model/GetExperimentConfigOutput.hs b/clients/haskell/sdk/Io/Superposition/Model/GetExperimentConfigOutput.hs new file mode 100644 index 000000000..3e7a39872 --- /dev/null +++ b/clients/haskell/sdk/Io/Superposition/Model/GetExperimentConfigOutput.hs @@ -0,0 +1,110 @@ +module Io.Superposition.Model.GetExperimentConfigOutput ( + setLastModified, + setExperiments, + setExperimentGroups, + build, + GetExperimentConfigOutputBuilder, + GetExperimentConfigOutput, + last_modified, + experiments, + experiment_groups +) where +import qualified Control.Applicative +import qualified Control.Monad.State.Strict +import qualified Data.Aeson +import qualified Data.Either +import qualified Data.Eq +import qualified Data.Functor +import qualified Data.Maybe +import qualified Data.Text +import qualified Data.Time +import qualified GHC.Generics +import qualified GHC.Show +import qualified Io.Superposition.Model.ExperimentGroupResponse +import qualified Io.Superposition.Model.ExperimentResponse +import qualified Io.Superposition.Utility +import qualified Network.HTTP.Types + +data GetExperimentConfigOutput = GetExperimentConfigOutput { + last_modified :: Data.Time.UTCTime, + experiments :: [] Io.Superposition.Model.ExperimentResponse.ExperimentResponse, + experiment_groups :: [] Io.Superposition.Model.ExperimentGroupResponse.ExperimentGroupResponse +} deriving ( + GHC.Show.Show, + Data.Eq.Eq, + GHC.Generics.Generic + ) + +instance Data.Aeson.ToJSON GetExperimentConfigOutput where + toJSON a = Data.Aeson.object [ + "last_modified" Data.Aeson..= last_modified a, + "experiments" Data.Aeson..= experiments a, + "experiment_groups" Data.Aeson..= experiment_groups a + ] + + +instance Io.Superposition.Utility.SerializeBody GetExperimentConfigOutput + +instance Data.Aeson.FromJSON GetExperimentConfigOutput where + parseJSON = Data.Aeson.withObject "GetExperimentConfigOutput" $ \v -> GetExperimentConfigOutput + Data.Functor.<$> (v Data.Aeson..: "last_modified") + Control.Applicative.<*> (v Data.Aeson..: "experiments") + Control.Applicative.<*> (v Data.Aeson..: "experiment_groups") + + + + +data GetExperimentConfigOutputBuilderState = GetExperimentConfigOutputBuilderState { + last_modifiedBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime, + experimentsBuilderState :: Data.Maybe.Maybe ([] Io.Superposition.Model.ExperimentResponse.ExperimentResponse), + experiment_groupsBuilderState :: Data.Maybe.Maybe ([] Io.Superposition.Model.ExperimentGroupResponse.ExperimentGroupResponse) +} deriving ( + GHC.Generics.Generic + ) + +defaultBuilderState :: GetExperimentConfigOutputBuilderState +defaultBuilderState = GetExperimentConfigOutputBuilderState { + last_modifiedBuilderState = Data.Maybe.Nothing, + experimentsBuilderState = Data.Maybe.Nothing, + experiment_groupsBuilderState = Data.Maybe.Nothing +} + +type GetExperimentConfigOutputBuilder = Control.Monad.State.Strict.State GetExperimentConfigOutputBuilderState + +setLastModified :: Data.Time.UTCTime -> GetExperimentConfigOutputBuilder () +setLastModified value = + Control.Monad.State.Strict.modify (\s -> (s { last_modifiedBuilderState = Data.Maybe.Just value })) + +setExperiments :: [] Io.Superposition.Model.ExperimentResponse.ExperimentResponse -> GetExperimentConfigOutputBuilder () +setExperiments value = + Control.Monad.State.Strict.modify (\s -> (s { experimentsBuilderState = Data.Maybe.Just value })) + +setExperimentGroups :: [] Io.Superposition.Model.ExperimentGroupResponse.ExperimentGroupResponse -> GetExperimentConfigOutputBuilder () +setExperimentGroups value = + Control.Monad.State.Strict.modify (\s -> (s { experiment_groupsBuilderState = Data.Maybe.Just value })) + +build :: GetExperimentConfigOutputBuilder () -> Data.Either.Either Data.Text.Text GetExperimentConfigOutput +build builder = do + let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState + last_modified' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetExperimentConfigOutput.GetExperimentConfigOutput.last_modified is a required property.") Data.Either.Right (last_modifiedBuilderState st) + experiments' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetExperimentConfigOutput.GetExperimentConfigOutput.experiments is a required property.") Data.Either.Right (experimentsBuilderState st) + experiment_groups' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.GetExperimentConfigOutput.GetExperimentConfigOutput.experiment_groups is a required property.") Data.Either.Right (experiment_groupsBuilderState st) + Data.Either.Right (GetExperimentConfigOutput { + last_modified = last_modified', + experiments = experiments', + experiment_groups = experiment_groups' + }) + + +instance Io.Superposition.Utility.FromResponseParser GetExperimentConfigOutput where + expectedStatus = Network.HTTP.Types.status200 + responseParser = do + var0 <- Io.Superposition.Utility.deSerHeader "last-modified" + var1 <- Io.Superposition.Utility.deSerField "experiment_groups" + var2 <- Io.Superposition.Utility.deSerField "experiments" + pure $ GetExperimentConfigOutput { + last_modified = var0, + experiments = var2, + experiment_groups = var1 + } + diff --git a/clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsInput.hs b/clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsInput.hs index 956eaaaf8..ec2322a16 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsInput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsInput.hs @@ -4,12 +4,15 @@ module Io.Superposition.Model.ListExperimentGroupsInput ( setAll', setWorkspaceId, setOrgId, + setIfModifiedSince, setName, setCreatedBy, setLastModifiedBy, setSortOn, setSortBy, setGroupType, + setDimensionMatchStrategy, + setContext, build, ListExperimentGroupsInputBuilder, ListExperimentGroupsInput, @@ -18,12 +21,15 @@ module Io.Superposition.Model.ListExperimentGroupsInput ( all', workspace_id, org_id, + if_modified_since, name, created_by, last_modified_by, sort_on, sort_by, - group_type + group_type, + dimension_match_strategy, + context ) where import qualified Control.Applicative import qualified Control.Monad.State.Strict @@ -32,10 +38,13 @@ import qualified Data.Either import qualified Data.Eq import qualified Data.Functor import qualified Data.Int +import qualified Data.Map import qualified Data.Maybe import qualified Data.Text +import qualified Data.Time import qualified GHC.Generics import qualified GHC.Show +import qualified Io.Superposition.Model.DimensionMatchStrategy import qualified Io.Superposition.Model.ExperimentGroupSortOn import qualified Io.Superposition.Model.GroupType import qualified Io.Superposition.Model.SortBy @@ -48,12 +57,15 @@ data ListExperimentGroupsInput = ListExperimentGroupsInput { all' :: Data.Maybe.Maybe Bool, workspace_id :: Data.Text.Text, org_id :: Data.Text.Text, + if_modified_since :: Data.Maybe.Maybe Data.Time.UTCTime, name :: Data.Maybe.Maybe Data.Text.Text, created_by :: Data.Maybe.Maybe Data.Text.Text, last_modified_by :: Data.Maybe.Maybe Data.Text.Text, sort_on :: Data.Maybe.Maybe Io.Superposition.Model.ExperimentGroupSortOn.ExperimentGroupSortOn, sort_by :: Data.Maybe.Maybe Io.Superposition.Model.SortBy.SortBy, - group_type :: Data.Maybe.Maybe ([] Io.Superposition.Model.GroupType.GroupType) + group_type :: Data.Maybe.Maybe ([] Io.Superposition.Model.GroupType.GroupType), + dimension_match_strategy :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMatchStrategy.DimensionMatchStrategy, + context :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value) } deriving ( GHC.Show.Show, Data.Eq.Eq, @@ -67,12 +79,15 @@ instance Data.Aeson.ToJSON ListExperimentGroupsInput where "all" Data.Aeson..= all' a, "workspace_id" Data.Aeson..= workspace_id a, "org_id" Data.Aeson..= org_id a, + "if_modified_since" Data.Aeson..= if_modified_since a, "name" Data.Aeson..= name a, "created_by" Data.Aeson..= created_by a, "last_modified_by" Data.Aeson..= last_modified_by a, "sort_on" Data.Aeson..= sort_on a, "sort_by" Data.Aeson..= sort_by a, - "group_type" Data.Aeson..= group_type a + "group_type" Data.Aeson..= group_type a, + "dimension_match_strategy" Data.Aeson..= dimension_match_strategy a, + "context" Data.Aeson..= context a ] @@ -85,12 +100,15 @@ instance Data.Aeson.FromJSON ListExperimentGroupsInput where Control.Applicative.<*> (v Data.Aeson..:? "all") Control.Applicative.<*> (v Data.Aeson..: "workspace_id") Control.Applicative.<*> (v Data.Aeson..: "org_id") + Control.Applicative.<*> (v Data.Aeson..:? "if_modified_since") Control.Applicative.<*> (v Data.Aeson..:? "name") Control.Applicative.<*> (v Data.Aeson..:? "created_by") Control.Applicative.<*> (v Data.Aeson..:? "last_modified_by") Control.Applicative.<*> (v Data.Aeson..:? "sort_on") Control.Applicative.<*> (v Data.Aeson..:? "sort_by") Control.Applicative.<*> (v Data.Aeson..:? "group_type") + Control.Applicative.<*> (v Data.Aeson..:? "dimension_match_strategy") + Control.Applicative.<*> (v Data.Aeson..:? "context") @@ -101,12 +119,15 @@ data ListExperimentGroupsInputBuilderState = ListExperimentGroupsInputBuilderSta all'BuilderState :: Data.Maybe.Maybe Bool, workspace_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, + if_modified_sinceBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime, nameBuilderState :: Data.Maybe.Maybe Data.Text.Text, created_byBuilderState :: Data.Maybe.Maybe Data.Text.Text, last_modified_byBuilderState :: Data.Maybe.Maybe Data.Text.Text, sort_onBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ExperimentGroupSortOn.ExperimentGroupSortOn, sort_byBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.SortBy.SortBy, - group_typeBuilderState :: Data.Maybe.Maybe ([] Io.Superposition.Model.GroupType.GroupType) + group_typeBuilderState :: Data.Maybe.Maybe ([] Io.Superposition.Model.GroupType.GroupType), + dimension_match_strategyBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMatchStrategy.DimensionMatchStrategy, + contextBuilderState :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value) } deriving ( GHC.Generics.Generic ) @@ -118,12 +139,15 @@ defaultBuilderState = ListExperimentGroupsInputBuilderState { all'BuilderState = Data.Maybe.Nothing, workspace_idBuilderState = Data.Maybe.Nothing, org_idBuilderState = Data.Maybe.Nothing, + if_modified_sinceBuilderState = Data.Maybe.Nothing, nameBuilderState = Data.Maybe.Nothing, created_byBuilderState = Data.Maybe.Nothing, last_modified_byBuilderState = Data.Maybe.Nothing, sort_onBuilderState = Data.Maybe.Nothing, sort_byBuilderState = Data.Maybe.Nothing, - group_typeBuilderState = Data.Maybe.Nothing + group_typeBuilderState = Data.Maybe.Nothing, + dimension_match_strategyBuilderState = Data.Maybe.Nothing, + contextBuilderState = Data.Maybe.Nothing } type ListExperimentGroupsInputBuilder = Control.Monad.State.Strict.State ListExperimentGroupsInputBuilderState @@ -148,6 +172,10 @@ setOrgId :: Data.Text.Text -> ListExperimentGroupsInputBuilder () setOrgId value = Control.Monad.State.Strict.modify (\s -> (s { org_idBuilderState = Data.Maybe.Just value })) +setIfModifiedSince :: Data.Maybe.Maybe Data.Time.UTCTime -> ListExperimentGroupsInputBuilder () +setIfModifiedSince value = + Control.Monad.State.Strict.modify (\s -> (s { if_modified_sinceBuilderState = value })) + setName :: Data.Maybe.Maybe Data.Text.Text -> ListExperimentGroupsInputBuilder () setName value = Control.Monad.State.Strict.modify (\s -> (s { nameBuilderState = value })) @@ -172,6 +200,14 @@ setGroupType :: Data.Maybe.Maybe ([] Io.Superposition.Model.GroupType.GroupType) setGroupType value = Control.Monad.State.Strict.modify (\s -> (s { group_typeBuilderState = value })) +setDimensionMatchStrategy :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMatchStrategy.DimensionMatchStrategy -> ListExperimentGroupsInputBuilder () +setDimensionMatchStrategy value = + Control.Monad.State.Strict.modify (\s -> (s { dimension_match_strategyBuilderState = value })) + +setContext :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value) -> ListExperimentGroupsInputBuilder () +setContext value = + Control.Monad.State.Strict.modify (\s -> (s { contextBuilderState = value })) + build :: ListExperimentGroupsInputBuilder () -> Data.Either.Either Data.Text.Text ListExperimentGroupsInput build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState @@ -180,43 +216,52 @@ build builder = do all'' <- Data.Either.Right (all'BuilderState st) workspace_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentGroupsInput.ListExperimentGroupsInput.workspace_id is a required property.") Data.Either.Right (workspace_idBuilderState st) org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentGroupsInput.ListExperimentGroupsInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) + if_modified_since' <- Data.Either.Right (if_modified_sinceBuilderState st) name' <- Data.Either.Right (nameBuilderState st) created_by' <- Data.Either.Right (created_byBuilderState st) last_modified_by' <- Data.Either.Right (last_modified_byBuilderState st) sort_on' <- Data.Either.Right (sort_onBuilderState st) sort_by' <- Data.Either.Right (sort_byBuilderState st) group_type' <- Data.Either.Right (group_typeBuilderState st) + dimension_match_strategy' <- Data.Either.Right (dimension_match_strategyBuilderState st) + context' <- Data.Either.Right (contextBuilderState st) Data.Either.Right (ListExperimentGroupsInput { count = count', page = page', all' = all'', workspace_id = workspace_id', org_id = org_id', + if_modified_since = if_modified_since', name = name', created_by = created_by', last_modified_by = last_modified_by', sort_on = sort_on', sort_by = sort_by', - group_type = group_type' + group_type = group_type', + dimension_match_strategy = dimension_match_strategy', + context = context' }) instance Io.Superposition.Utility.IntoRequestBuilder ListExperimentGroupsInput where intoRequestBuilder self = do - Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodGet + Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodPost Io.Superposition.Utility.setPath [ - "experiment-groups" + "experiment-groups", + "list" ] Io.Superposition.Utility.serQuery "all" (all' self) - Io.Superposition.Utility.serQuery "sort_on" (sort_on self) Io.Superposition.Utility.serQuery "count" (count self) - Io.Superposition.Utility.serQuery "name" (name self) - Io.Superposition.Utility.serQuery "page" (page self) Io.Superposition.Utility.serQuery "last_modified_by" (last_modified_by self) Io.Superposition.Utility.serQuery "sort_by" (sort_by self) Io.Superposition.Utility.serQuery "group_type" (group_type self) Io.Superposition.Utility.serQuery "created_by" (created_by self) + Io.Superposition.Utility.serQuery "dimension_match_strategy" (dimension_match_strategy self) + Io.Superposition.Utility.serQuery "sort_on" (sort_on self) + Io.Superposition.Utility.serQuery "name" (name self) + Io.Superposition.Utility.serQuery "page" (page self) + Io.Superposition.Utility.serHeader "if-modified-since" (if_modified_since self) Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) Io.Superposition.Utility.serHeader "x-org-id" (org_id self) - + Io.Superposition.Utility.serField "context" (context self) diff --git a/clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsOutput.hs b/clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsOutput.hs index 0bdd59132..58a01a451 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsOutput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/ListExperimentGroupsOutput.hs @@ -2,12 +2,14 @@ module Io.Superposition.Model.ListExperimentGroupsOutput ( setTotalPages, setTotalItems, setData', + setLastModified, build, ListExperimentGroupsOutputBuilder, ListExperimentGroupsOutput, total_pages, total_items, - data' + data', + last_modified ) where import qualified Control.Applicative import qualified Control.Monad.State.Strict @@ -18,6 +20,7 @@ import qualified Data.Functor import qualified Data.Int import qualified Data.Maybe import qualified Data.Text +import qualified Data.Time import qualified GHC.Generics import qualified GHC.Show import qualified Io.Superposition.Model.ExperimentGroupResponse @@ -27,7 +30,8 @@ import qualified Network.HTTP.Types data ListExperimentGroupsOutput = ListExperimentGroupsOutput { total_pages :: Data.Int.Int32, total_items :: Data.Int.Int32, - data' :: [] Io.Superposition.Model.ExperimentGroupResponse.ExperimentGroupResponse + data' :: [] Io.Superposition.Model.ExperimentGroupResponse.ExperimentGroupResponse, + last_modified :: Data.Time.UTCTime } deriving ( GHC.Show.Show, Data.Eq.Eq, @@ -38,7 +42,8 @@ instance Data.Aeson.ToJSON ListExperimentGroupsOutput where toJSON a = Data.Aeson.object [ "total_pages" Data.Aeson..= total_pages a, "total_items" Data.Aeson..= total_items a, - "data" Data.Aeson..= data' a + "data" Data.Aeson..= data' a, + "last_modified" Data.Aeson..= last_modified a ] @@ -49,6 +54,7 @@ instance Data.Aeson.FromJSON ListExperimentGroupsOutput where Data.Functor.<$> (v Data.Aeson..: "total_pages") Control.Applicative.<*> (v Data.Aeson..: "total_items") Control.Applicative.<*> (v Data.Aeson..: "data") + Control.Applicative.<*> (v Data.Aeson..: "last_modified") @@ -56,7 +62,8 @@ instance Data.Aeson.FromJSON ListExperimentGroupsOutput where data ListExperimentGroupsOutputBuilderState = ListExperimentGroupsOutputBuilderState { total_pagesBuilderState :: Data.Maybe.Maybe Data.Int.Int32, total_itemsBuilderState :: Data.Maybe.Maybe Data.Int.Int32, - data'BuilderState :: Data.Maybe.Maybe ([] Io.Superposition.Model.ExperimentGroupResponse.ExperimentGroupResponse) + data'BuilderState :: Data.Maybe.Maybe ([] Io.Superposition.Model.ExperimentGroupResponse.ExperimentGroupResponse), + last_modifiedBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime } deriving ( GHC.Generics.Generic ) @@ -65,7 +72,8 @@ defaultBuilderState :: ListExperimentGroupsOutputBuilderState defaultBuilderState = ListExperimentGroupsOutputBuilderState { total_pagesBuilderState = Data.Maybe.Nothing, total_itemsBuilderState = Data.Maybe.Nothing, - data'BuilderState = Data.Maybe.Nothing + data'BuilderState = Data.Maybe.Nothing, + last_modifiedBuilderState = Data.Maybe.Nothing } type ListExperimentGroupsOutputBuilder = Control.Monad.State.Strict.State ListExperimentGroupsOutputBuilderState @@ -82,29 +90,36 @@ setData' :: [] Io.Superposition.Model.ExperimentGroupResponse.ExperimentGroupRes setData' value = Control.Monad.State.Strict.modify (\s -> (s { data'BuilderState = Data.Maybe.Just value })) +setLastModified :: Data.Time.UTCTime -> ListExperimentGroupsOutputBuilder () +setLastModified value = + Control.Monad.State.Strict.modify (\s -> (s { last_modifiedBuilderState = Data.Maybe.Just value })) + build :: ListExperimentGroupsOutputBuilder () -> Data.Either.Either Data.Text.Text ListExperimentGroupsOutput build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState total_pages' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentGroupsOutput.ListExperimentGroupsOutput.total_pages is a required property.") Data.Either.Right (total_pagesBuilderState st) total_items' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentGroupsOutput.ListExperimentGroupsOutput.total_items is a required property.") Data.Either.Right (total_itemsBuilderState st) data'' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentGroupsOutput.ListExperimentGroupsOutput.data' is a required property.") Data.Either.Right (data'BuilderState st) + last_modified' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentGroupsOutput.ListExperimentGroupsOutput.last_modified is a required property.") Data.Either.Right (last_modifiedBuilderState st) Data.Either.Right (ListExperimentGroupsOutput { total_pages = total_pages', total_items = total_items', - data' = data'' + data' = data'', + last_modified = last_modified' }) instance Io.Superposition.Utility.FromResponseParser ListExperimentGroupsOutput where expectedStatus = Network.HTTP.Types.status200 responseParser = do - - var0 <- Io.Superposition.Utility.deSerField "data" - var1 <- Io.Superposition.Utility.deSerField "total_pages" - var2 <- Io.Superposition.Utility.deSerField "total_items" + var0 <- Io.Superposition.Utility.deSerHeader "last-modified" + var1 <- Io.Superposition.Utility.deSerField "data" + var2 <- Io.Superposition.Utility.deSerField "total_pages" + var3 <- Io.Superposition.Utility.deSerField "total_items" pure $ ListExperimentGroupsOutput { - total_pages = var1, - total_items = var2, - data' = var0 + total_pages = var2, + total_items = var3, + data' = var1, + last_modified = var0 } diff --git a/clients/haskell/sdk/Io/Superposition/Model/ListExperimentInput.hs b/clients/haskell/sdk/Io/Superposition/Model/ListExperimentInput.hs index 557a55527..49d8949cd 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/ListExperimentInput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/ListExperimentInput.hs @@ -4,6 +4,7 @@ module Io.Superposition.Model.ListExperimentInput ( setAll', setWorkspaceId, setOrgId, + setIfModifiedSince, setStatus, setFromDate, setToDate, @@ -15,6 +16,8 @@ module Io.Superposition.Model.ListExperimentInput ( setSortBy, setGlobalExperimentsOnly, setDimensionMatchStrategy, + setPrefix, + setContext, build, ListExperimentInputBuilder, ListExperimentInput, @@ -23,6 +26,7 @@ module Io.Superposition.Model.ListExperimentInput ( all', workspace_id, org_id, + if_modified_since, status, from_date, to_date, @@ -33,7 +37,9 @@ module Io.Superposition.Model.ListExperimentInput ( sort_on, sort_by, global_experiments_only, - dimension_match_strategy + dimension_match_strategy, + prefix, + context ) where import qualified Control.Applicative import qualified Control.Monad.State.Strict @@ -42,6 +48,7 @@ import qualified Data.Either import qualified Data.Eq import qualified Data.Functor import qualified Data.Int +import qualified Data.Map import qualified Data.Maybe import qualified Data.Text import qualified Data.Time @@ -60,6 +67,7 @@ data ListExperimentInput = ListExperimentInput { all' :: Data.Maybe.Maybe Bool, workspace_id :: Data.Text.Text, org_id :: Data.Text.Text, + if_modified_since :: Data.Maybe.Maybe Data.Time.UTCTime, status :: Data.Maybe.Maybe ([] Io.Superposition.Model.ExperimentStatusType.ExperimentStatusType), from_date :: Data.Maybe.Maybe Data.Time.UTCTime, to_date :: Data.Maybe.Maybe Data.Time.UTCTime, @@ -70,7 +78,9 @@ data ListExperimentInput = ListExperimentInput { sort_on :: Data.Maybe.Maybe Io.Superposition.Model.ExperimentSortOn.ExperimentSortOn, sort_by :: Data.Maybe.Maybe Io.Superposition.Model.SortBy.SortBy, global_experiments_only :: Data.Maybe.Maybe Bool, - dimension_match_strategy :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMatchStrategy.DimensionMatchStrategy + dimension_match_strategy :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMatchStrategy.DimensionMatchStrategy, + prefix :: Data.Maybe.Maybe ([] Data.Text.Text), + context :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value) } deriving ( GHC.Show.Show, Data.Eq.Eq, @@ -84,6 +94,7 @@ instance Data.Aeson.ToJSON ListExperimentInput where "all" Data.Aeson..= all' a, "workspace_id" Data.Aeson..= workspace_id a, "org_id" Data.Aeson..= org_id a, + "if_modified_since" Data.Aeson..= if_modified_since a, "status" Data.Aeson..= status a, "from_date" Data.Aeson..= from_date a, "to_date" Data.Aeson..= to_date a, @@ -94,7 +105,9 @@ instance Data.Aeson.ToJSON ListExperimentInput where "sort_on" Data.Aeson..= sort_on a, "sort_by" Data.Aeson..= sort_by a, "global_experiments_only" Data.Aeson..= global_experiments_only a, - "dimension_match_strategy" Data.Aeson..= dimension_match_strategy a + "dimension_match_strategy" Data.Aeson..= dimension_match_strategy a, + "prefix" Data.Aeson..= prefix a, + "context" Data.Aeson..= context a ] @@ -107,6 +120,7 @@ instance Data.Aeson.FromJSON ListExperimentInput where Control.Applicative.<*> (v Data.Aeson..:? "all") Control.Applicative.<*> (v Data.Aeson..: "workspace_id") Control.Applicative.<*> (v Data.Aeson..: "org_id") + Control.Applicative.<*> (v Data.Aeson..:? "if_modified_since") Control.Applicative.<*> (v Data.Aeson..:? "status") Control.Applicative.<*> (v Data.Aeson..:? "from_date") Control.Applicative.<*> (v Data.Aeson..:? "to_date") @@ -118,6 +132,8 @@ instance Data.Aeson.FromJSON ListExperimentInput where Control.Applicative.<*> (v Data.Aeson..:? "sort_by") Control.Applicative.<*> (v Data.Aeson..:? "global_experiments_only") Control.Applicative.<*> (v Data.Aeson..:? "dimension_match_strategy") + Control.Applicative.<*> (v Data.Aeson..:? "prefix") + Control.Applicative.<*> (v Data.Aeson..:? "context") @@ -128,6 +144,7 @@ data ListExperimentInputBuilderState = ListExperimentInputBuilderState { all'BuilderState :: Data.Maybe.Maybe Bool, workspace_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, org_idBuilderState :: Data.Maybe.Maybe Data.Text.Text, + if_modified_sinceBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime, statusBuilderState :: Data.Maybe.Maybe ([] Io.Superposition.Model.ExperimentStatusType.ExperimentStatusType), from_dateBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime, to_dateBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime, @@ -138,7 +155,9 @@ data ListExperimentInputBuilderState = ListExperimentInputBuilderState { sort_onBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.ExperimentSortOn.ExperimentSortOn, sort_byBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.SortBy.SortBy, global_experiments_onlyBuilderState :: Data.Maybe.Maybe Bool, - dimension_match_strategyBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMatchStrategy.DimensionMatchStrategy + dimension_match_strategyBuilderState :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMatchStrategy.DimensionMatchStrategy, + prefixBuilderState :: Data.Maybe.Maybe ([] Data.Text.Text), + contextBuilderState :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value) } deriving ( GHC.Generics.Generic ) @@ -150,6 +169,7 @@ defaultBuilderState = ListExperimentInputBuilderState { all'BuilderState = Data.Maybe.Nothing, workspace_idBuilderState = Data.Maybe.Nothing, org_idBuilderState = Data.Maybe.Nothing, + if_modified_sinceBuilderState = Data.Maybe.Nothing, statusBuilderState = Data.Maybe.Nothing, from_dateBuilderState = Data.Maybe.Nothing, to_dateBuilderState = Data.Maybe.Nothing, @@ -160,7 +180,9 @@ defaultBuilderState = ListExperimentInputBuilderState { sort_onBuilderState = Data.Maybe.Nothing, sort_byBuilderState = Data.Maybe.Nothing, global_experiments_onlyBuilderState = Data.Maybe.Nothing, - dimension_match_strategyBuilderState = Data.Maybe.Nothing + dimension_match_strategyBuilderState = Data.Maybe.Nothing, + prefixBuilderState = Data.Maybe.Nothing, + contextBuilderState = Data.Maybe.Nothing } type ListExperimentInputBuilder = Control.Monad.State.Strict.State ListExperimentInputBuilderState @@ -185,6 +207,10 @@ setOrgId :: Data.Text.Text -> ListExperimentInputBuilder () setOrgId value = Control.Monad.State.Strict.modify (\s -> (s { org_idBuilderState = Data.Maybe.Just value })) +setIfModifiedSince :: Data.Maybe.Maybe Data.Time.UTCTime -> ListExperimentInputBuilder () +setIfModifiedSince value = + Control.Monad.State.Strict.modify (\s -> (s { if_modified_sinceBuilderState = value })) + setStatus :: Data.Maybe.Maybe ([] Io.Superposition.Model.ExperimentStatusType.ExperimentStatusType) -> ListExperimentInputBuilder () setStatus value = Control.Monad.State.Strict.modify (\s -> (s { statusBuilderState = value })) @@ -229,6 +255,14 @@ setDimensionMatchStrategy :: Data.Maybe.Maybe Io.Superposition.Model.DimensionMa setDimensionMatchStrategy value = Control.Monad.State.Strict.modify (\s -> (s { dimension_match_strategyBuilderState = value })) +setPrefix :: Data.Maybe.Maybe ([] Data.Text.Text) -> ListExperimentInputBuilder () +setPrefix value = + Control.Monad.State.Strict.modify (\s -> (s { prefixBuilderState = value })) + +setContext :: Data.Maybe.Maybe (Data.Map.Map Data.Text.Text Data.Aeson.Value) -> ListExperimentInputBuilder () +setContext value = + Control.Monad.State.Strict.modify (\s -> (s { contextBuilderState = value })) + build :: ListExperimentInputBuilder () -> Data.Either.Either Data.Text.Text ListExperimentInput build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState @@ -237,6 +271,7 @@ build builder = do all'' <- Data.Either.Right (all'BuilderState st) workspace_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentInput.ListExperimentInput.workspace_id is a required property.") Data.Either.Right (workspace_idBuilderState st) org_id' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentInput.ListExperimentInput.org_id is a required property.") Data.Either.Right (org_idBuilderState st) + if_modified_since' <- Data.Either.Right (if_modified_sinceBuilderState st) status' <- Data.Either.Right (statusBuilderState st) from_date' <- Data.Either.Right (from_dateBuilderState st) to_date' <- Data.Either.Right (to_dateBuilderState st) @@ -248,12 +283,15 @@ build builder = do sort_by' <- Data.Either.Right (sort_byBuilderState st) global_experiments_only' <- Data.Either.Right (global_experiments_onlyBuilderState st) dimension_match_strategy' <- Data.Either.Right (dimension_match_strategyBuilderState st) + prefix' <- Data.Either.Right (prefixBuilderState st) + context' <- Data.Either.Right (contextBuilderState st) Data.Either.Right (ListExperimentInput { count = count', page = page', all' = all'', workspace_id = workspace_id', org_id = org_id', + if_modified_since = if_modified_since', status = status', from_date = from_date', to_date = to_date', @@ -264,19 +302,23 @@ build builder = do sort_on = sort_on', sort_by = sort_by', global_experiments_only = global_experiments_only', - dimension_match_strategy = dimension_match_strategy' + dimension_match_strategy = dimension_match_strategy', + prefix = prefix', + context = context' }) instance Io.Superposition.Utility.IntoRequestBuilder ListExperimentInput where intoRequestBuilder self = do - Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodGet + Io.Superposition.Utility.setMethod Network.HTTP.Types.Method.methodPost Io.Superposition.Utility.setPath [ - "experiments" + "experiments", + "list" ] Io.Superposition.Utility.serQuery "all" (all' self) Io.Superposition.Utility.serQuery "experiment_name" (experiment_name self) Io.Superposition.Utility.serQuery "from_date" (from_date self) + Io.Superposition.Utility.serQuery "prefix" (prefix self) Io.Superposition.Utility.serQuery "count" (count self) Io.Superposition.Utility.serQuery "experiment_ids" (experiment_ids self) Io.Superposition.Utility.serQuery "global_experiments_only" (global_experiments_only self) @@ -288,7 +330,8 @@ instance Io.Superposition.Utility.IntoRequestBuilder ListExperimentInput where Io.Superposition.Utility.serQuery "to_date" (to_date self) Io.Superposition.Utility.serQuery "page" (page self) Io.Superposition.Utility.serQuery "status" (status self) + Io.Superposition.Utility.serHeader "if-modified-since" (if_modified_since self) Io.Superposition.Utility.serHeader "x-workspace" (workspace_id self) Io.Superposition.Utility.serHeader "x-org-id" (org_id self) - + Io.Superposition.Utility.serField "context" (context self) diff --git a/clients/haskell/sdk/Io/Superposition/Model/ListExperimentOutput.hs b/clients/haskell/sdk/Io/Superposition/Model/ListExperimentOutput.hs index cb1afa39a..03a0aa25b 100644 --- a/clients/haskell/sdk/Io/Superposition/Model/ListExperimentOutput.hs +++ b/clients/haskell/sdk/Io/Superposition/Model/ListExperimentOutput.hs @@ -2,12 +2,14 @@ module Io.Superposition.Model.ListExperimentOutput ( setTotalPages, setTotalItems, setData', + setLastModified, build, ListExperimentOutputBuilder, ListExperimentOutput, total_pages, total_items, - data' + data', + last_modified ) where import qualified Control.Applicative import qualified Control.Monad.State.Strict @@ -18,6 +20,7 @@ import qualified Data.Functor import qualified Data.Int import qualified Data.Maybe import qualified Data.Text +import qualified Data.Time import qualified GHC.Generics import qualified GHC.Show import qualified Io.Superposition.Model.ExperimentResponse @@ -27,7 +30,8 @@ import qualified Network.HTTP.Types data ListExperimentOutput = ListExperimentOutput { total_pages :: Data.Int.Int32, total_items :: Data.Int.Int32, - data' :: [] Io.Superposition.Model.ExperimentResponse.ExperimentResponse + data' :: [] Io.Superposition.Model.ExperimentResponse.ExperimentResponse, + last_modified :: Data.Time.UTCTime } deriving ( GHC.Show.Show, Data.Eq.Eq, @@ -38,7 +42,8 @@ instance Data.Aeson.ToJSON ListExperimentOutput where toJSON a = Data.Aeson.object [ "total_pages" Data.Aeson..= total_pages a, "total_items" Data.Aeson..= total_items a, - "data" Data.Aeson..= data' a + "data" Data.Aeson..= data' a, + "last_modified" Data.Aeson..= last_modified a ] @@ -49,6 +54,7 @@ instance Data.Aeson.FromJSON ListExperimentOutput where Data.Functor.<$> (v Data.Aeson..: "total_pages") Control.Applicative.<*> (v Data.Aeson..: "total_items") Control.Applicative.<*> (v Data.Aeson..: "data") + Control.Applicative.<*> (v Data.Aeson..: "last_modified") @@ -56,7 +62,8 @@ instance Data.Aeson.FromJSON ListExperimentOutput where data ListExperimentOutputBuilderState = ListExperimentOutputBuilderState { total_pagesBuilderState :: Data.Maybe.Maybe Data.Int.Int32, total_itemsBuilderState :: Data.Maybe.Maybe Data.Int.Int32, - data'BuilderState :: Data.Maybe.Maybe ([] Io.Superposition.Model.ExperimentResponse.ExperimentResponse) + data'BuilderState :: Data.Maybe.Maybe ([] Io.Superposition.Model.ExperimentResponse.ExperimentResponse), + last_modifiedBuilderState :: Data.Maybe.Maybe Data.Time.UTCTime } deriving ( GHC.Generics.Generic ) @@ -65,7 +72,8 @@ defaultBuilderState :: ListExperimentOutputBuilderState defaultBuilderState = ListExperimentOutputBuilderState { total_pagesBuilderState = Data.Maybe.Nothing, total_itemsBuilderState = Data.Maybe.Nothing, - data'BuilderState = Data.Maybe.Nothing + data'BuilderState = Data.Maybe.Nothing, + last_modifiedBuilderState = Data.Maybe.Nothing } type ListExperimentOutputBuilder = Control.Monad.State.Strict.State ListExperimentOutputBuilderState @@ -82,29 +90,36 @@ setData' :: [] Io.Superposition.Model.ExperimentResponse.ExperimentResponse -> L setData' value = Control.Monad.State.Strict.modify (\s -> (s { data'BuilderState = Data.Maybe.Just value })) +setLastModified :: Data.Time.UTCTime -> ListExperimentOutputBuilder () +setLastModified value = + Control.Monad.State.Strict.modify (\s -> (s { last_modifiedBuilderState = Data.Maybe.Just value })) + build :: ListExperimentOutputBuilder () -> Data.Either.Either Data.Text.Text ListExperimentOutput build builder = do let (_, st) = Control.Monad.State.Strict.runState builder defaultBuilderState total_pages' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentOutput.ListExperimentOutput.total_pages is a required property.") Data.Either.Right (total_pagesBuilderState st) total_items' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentOutput.ListExperimentOutput.total_items is a required property.") Data.Either.Right (total_itemsBuilderState st) data'' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentOutput.ListExperimentOutput.data' is a required property.") Data.Either.Right (data'BuilderState st) + last_modified' <- Data.Maybe.maybe (Data.Either.Left "Io.Superposition.Model.ListExperimentOutput.ListExperimentOutput.last_modified is a required property.") Data.Either.Right (last_modifiedBuilderState st) Data.Either.Right (ListExperimentOutput { total_pages = total_pages', total_items = total_items', - data' = data'' + data' = data'', + last_modified = last_modified' }) instance Io.Superposition.Utility.FromResponseParser ListExperimentOutput where expectedStatus = Network.HTTP.Types.status200 responseParser = do - - var0 <- Io.Superposition.Utility.deSerField "data" - var1 <- Io.Superposition.Utility.deSerField "total_pages" - var2 <- Io.Superposition.Utility.deSerField "total_items" + var0 <- Io.Superposition.Utility.deSerHeader "last-modified" + var1 <- Io.Superposition.Utility.deSerField "data" + var2 <- Io.Superposition.Utility.deSerField "total_pages" + var3 <- Io.Superposition.Utility.deSerField "total_items" pure $ ListExperimentOutput { - total_pages = var1, - total_items = var2, - data' = var0 + total_pages = var2, + total_items = var3, + data' = var1, + last_modified = var0 } diff --git a/clients/haskell/sdk/SuperpositionSDK.cabal b/clients/haskell/sdk/SuperpositionSDK.cabal index 5bc28b4b0..648cf0c78 100644 --- a/clients/haskell/sdk/SuperpositionSDK.cabal +++ b/clients/haskell/sdk/SuperpositionSDK.cabal @@ -47,9 +47,9 @@ library Io.Superposition.Command.CreateDimension, Io.Superposition.Model.ContextMove, Io.Superposition.Model.ExperimentType, - Io.Superposition.Model.BulkOperationOutput, - Io.Superposition.Model.ContextPartial, Io.Superposition.Model.GetVersionInput, + Io.Superposition.Model.ContextPartial, + Io.Superposition.Model.BulkOperationOutput, Io.Superposition.Command.Publish, Io.Superposition.Command.UpdateExperimentGroup, Io.Superposition.Model.BulkOperationInput, @@ -57,7 +57,6 @@ library Io.Superposition.Model.UpdateExperimentGroupInput, Io.Superposition.Command.ListFunction, Io.Superposition.Command.ConcludeExperiment, - Io.Superposition.Model.GetConfigFastInput, Io.Superposition.Command.CreateOrganisation, Io.Superposition.Model.DeleteDimensionOutput, Io.Superposition.Model.ListVersionsMember, @@ -88,7 +87,6 @@ library Io.Superposition.Model.GetResolvedConfigWithIdentifierInput, Io.Superposition.Model.DeleteVariableInput, Io.Superposition.Model.CreateDimensionOutput, - Io.Superposition.Command.GetConfigFast, Io.Superposition.Model.GetConfigInput, Io.Superposition.Command.ListWebhook, Io.Superposition.Model.CreateContextOutput, @@ -170,6 +168,7 @@ library Io.Superposition.Model.CreateDefaultConfigInput, Io.Superposition.Command.GetTypeTemplatesList, Io.Superposition.Model.AddMembersToGroupInput, + Io.Superposition.Model.GetExperimentConfigInput, Io.Superposition.Model.ValueComputeFunctionRequest, Io.Superposition.Model.GetDefaultConfigInput, Io.Superposition.Model.DimensionType, @@ -260,6 +259,7 @@ library Io.Superposition.Model.CreateOrganisationInput, Io.Superposition.Command.DeleteContext, Io.Superposition.Model.UpdateVariableInput, + Io.Superposition.Command.GetExperimentConfig, Io.Superposition.Model.ListSecretsInput, Io.Superposition.Model.ContextValidationFunctionRequest, Io.Superposition.Model.GetWebhookByEventOutput, @@ -269,7 +269,6 @@ library Io.Superposition.Model.ExperimentResponse, Io.Superposition.Model.GetTypeTemplatesListInput, Io.Superposition.Command.ListWorkspace, - Io.Superposition.Model.GetConfigFastOutput, Io.Superposition.Model.OrganisationResponse, Io.Superposition.Model.DimensionResponse, Io.Superposition.Model.FunctionResponse, @@ -325,6 +324,7 @@ library Io.Superposition.Model.Version, Io.Superposition.Model.ListContextsInput, Io.Superposition.Model.ListVariablesInput, + Io.Superposition.Model.GetExperimentConfigOutput, Io.Superposition.Model.MigrateWorkspaceSchemaOutput, Io.Superposition.Model.GetContextFromConditionOutput, Io.Superposition.Model.ChangeReasonValidationFunctionRequest, diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java index cd2b01640..5b4fbda9a 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClient.java @@ -53,8 +53,6 @@ import io.juspay.superposition.model.DeleteWebhookOutput; import io.juspay.superposition.model.DiscardExperimentInput; import io.juspay.superposition.model.DiscardExperimentOutput; -import io.juspay.superposition.model.GetConfigFastInput; -import io.juspay.superposition.model.GetConfigFastOutput; import io.juspay.superposition.model.GetConfigInput; import io.juspay.superposition.model.GetConfigJsonInput; import io.juspay.superposition.model.GetConfigJsonOutput; @@ -69,6 +67,8 @@ import io.juspay.superposition.model.GetDefaultConfigOutput; import io.juspay.superposition.model.GetDimensionInput; import io.juspay.superposition.model.GetDimensionOutput; +import io.juspay.superposition.model.GetExperimentConfigInput; +import io.juspay.superposition.model.GetExperimentConfigOutput; import io.juspay.superposition.model.GetExperimentGroupInput; import io.juspay.superposition.model.GetExperimentGroupOutput; import io.juspay.superposition.model.GetExperimentInput; @@ -694,22 +694,6 @@ default CompletableFuture getConfig(GetConfigInput input) { */ CompletableFuture getConfig(GetConfigInput input, RequestOverrideConfig overrideConfig); - /** - * Retrieves the latest config with no processing for high-performance access. - * - * @throws InternalServerError - */ - default CompletableFuture getConfigFast(GetConfigFastInput input) { - return getConfigFast(input, null); - } - - /** - * Retrieves the latest config with no processing for high-performance access. - * - * @throws InternalServerError - */ - CompletableFuture getConfigFast(GetConfigFastInput input, RequestOverrideConfig overrideConfig); - /** * Retrieves the full config in JSON format, including default configs with schemas, dimensions, and * overrides. This endpoint is optimized for clients that prefer JSON format for configuration @@ -850,6 +834,24 @@ default CompletableFuture getExperiment(GetExperimentInput */ CompletableFuture getExperiment(GetExperimentInput input, RequestOverrideConfig overrideConfig); + /** + * Retrieves the experiment configuration for a given workspace and organization. The response includes + * details of all experiment groups and experiments that match the specified filters. + * + * @throws InternalServerError + */ + default CompletableFuture getExperimentConfig(GetExperimentConfigInput input) { + return getExperimentConfig(input, null); + } + + /** + * Retrieves the experiment configuration for a given workspace and organization. The response includes + * details of all experiment groups and experiments that match the specified filters. + * + * @throws InternalServerError + */ + CompletableFuture getExperimentConfig(GetExperimentConfigInput input, RequestOverrideConfig overrideConfig); + /** * Retrieves an existing experiment group by its ID. * @@ -1817,12 +1819,12 @@ final class Builder extends Client.Builder { Node.objectNode() ); - private static final HttpBasicAuthTrait httpBasicAuthScheme = new HttpBasicAuthTrait(); - private static final AuthSchemeFactory httpBasicAuthSchemeFactory = new HttpBasicAuthAuthScheme.Factory(); - private static final HttpBearerAuthTrait httpBearerAuthScheme = new HttpBearerAuthTrait(); private static final AuthSchemeFactory httpBearerAuthSchemeFactory = new HttpBearerAuthScheme.Factory(); + private static final HttpBasicAuthTrait httpBasicAuthScheme = new HttpBasicAuthTrait(); + private static final AuthSchemeFactory httpBasicAuthSchemeFactory = new HttpBasicAuthAuthScheme.Factory(); + private Builder() { configBuilder().putSupportedAuthSchemes(httpBasicAuthSchemeFactory.createAuthScheme(httpBasicAuthScheme), httpBearerAuthSchemeFactory.createAuthScheme(httpBearerAuthScheme)); } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java index fce3fe6b9..79b07bff2 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionAsyncClientImpl.java @@ -80,9 +80,6 @@ import io.juspay.superposition.model.DiscardExperimentInput; import io.juspay.superposition.model.DiscardExperimentOutput; import io.juspay.superposition.model.GetConfig; -import io.juspay.superposition.model.GetConfigFast; -import io.juspay.superposition.model.GetConfigFastInput; -import io.juspay.superposition.model.GetConfigFastOutput; import io.juspay.superposition.model.GetConfigInput; import io.juspay.superposition.model.GetConfigJson; import io.juspay.superposition.model.GetConfigJsonInput; @@ -104,6 +101,9 @@ import io.juspay.superposition.model.GetDimensionInput; import io.juspay.superposition.model.GetDimensionOutput; import io.juspay.superposition.model.GetExperiment; +import io.juspay.superposition.model.GetExperimentConfig; +import io.juspay.superposition.model.GetExperimentConfigInput; +import io.juspay.superposition.model.GetExperimentConfigOutput; import io.juspay.superposition.model.GetExperimentGroup; import io.juspay.superposition.model.GetExperimentGroupInput; import io.juspay.superposition.model.GetExperimentGroupOutput; @@ -272,8 +272,8 @@ @SmithyGenerated final class SuperpositionAsyncClientImpl extends Client implements SuperpositionAsyncClient { private static final TypeRegistry TYPE_REGISTRY = TypeRegistry.builder() - .putType(AccessDeniedException.$ID, AccessDeniedException.class, AccessDeniedException::builder) .putType(ValidationException.$ID, ValidationException.class, ValidationException::builder) + .putType(AccessDeniedException.$ID, AccessDeniedException.class, AccessDeniedException::builder) .putType(NotAuthorizedException.$ID, NotAuthorizedException.class, NotAuthorizedException::builder) .putType(InternalFailureException.$ID, InternalFailureException.class, InternalFailureException::builder) .putType(UnknownOperationException.$ID, UnknownOperationException.class, UnknownOperationException::builder) @@ -393,10 +393,6 @@ final class SuperpositionAsyncClientImpl extends Client implements Superposition public CompletableFuture getConfig(GetConfigInput input, RequestOverrideConfig overrideConfig) {return call(input, GetConfig.instance(), overrideConfig); } - @Override - public CompletableFuture getConfigFast(GetConfigFastInput input, RequestOverrideConfig overrideConfig) {return call(input, GetConfigFast.instance(), overrideConfig); - } - @Override public CompletableFuture getConfigJson(GetConfigJsonInput input, RequestOverrideConfig overrideConfig) {return call(input, GetConfigJson.instance(), overrideConfig); } @@ -425,6 +421,10 @@ final class SuperpositionAsyncClientImpl extends Client implements Superposition public CompletableFuture getExperiment(GetExperimentInput input, RequestOverrideConfig overrideConfig) {return call(input, GetExperiment.instance(), overrideConfig); } + @Override + public CompletableFuture getExperimentConfig(GetExperimentConfigInput input, RequestOverrideConfig overrideConfig) {return call(input, GetExperimentConfig.instance(), overrideConfig); + } + @Override public CompletableFuture getExperimentGroup(GetExperimentGroupInput input, RequestOverrideConfig overrideConfig) {return call(input, GetExperimentGroup.instance(), overrideConfig); } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java index 716bcdacf..f18a231d0 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClient.java @@ -53,8 +53,6 @@ import io.juspay.superposition.model.DeleteWebhookOutput; import io.juspay.superposition.model.DiscardExperimentInput; import io.juspay.superposition.model.DiscardExperimentOutput; -import io.juspay.superposition.model.GetConfigFastInput; -import io.juspay.superposition.model.GetConfigFastOutput; import io.juspay.superposition.model.GetConfigInput; import io.juspay.superposition.model.GetConfigJsonInput; import io.juspay.superposition.model.GetConfigJsonOutput; @@ -69,6 +67,8 @@ import io.juspay.superposition.model.GetDefaultConfigOutput; import io.juspay.superposition.model.GetDimensionInput; import io.juspay.superposition.model.GetDimensionOutput; +import io.juspay.superposition.model.GetExperimentConfigInput; +import io.juspay.superposition.model.GetExperimentConfigOutput; import io.juspay.superposition.model.GetExperimentGroupInput; import io.juspay.superposition.model.GetExperimentGroupOutput; import io.juspay.superposition.model.GetExperimentInput; @@ -693,22 +693,6 @@ default GetConfigOutput getConfig(GetConfigInput input) { */ GetConfigOutput getConfig(GetConfigInput input, RequestOverrideConfig overrideConfig); - /** - * Retrieves the latest config with no processing for high-performance access. - * - * @throws InternalServerError - */ - default GetConfigFastOutput getConfigFast(GetConfigFastInput input) { - return getConfigFast(input, null); - } - - /** - * Retrieves the latest config with no processing for high-performance access. - * - * @throws InternalServerError - */ - GetConfigFastOutput getConfigFast(GetConfigFastInput input, RequestOverrideConfig overrideConfig); - /** * Retrieves the full config in JSON format, including default configs with schemas, dimensions, and * overrides. This endpoint is optimized for clients that prefer JSON format for configuration @@ -849,6 +833,24 @@ default GetExperimentOutput getExperiment(GetExperimentInput input) { */ GetExperimentOutput getExperiment(GetExperimentInput input, RequestOverrideConfig overrideConfig); + /** + * Retrieves the experiment configuration for a given workspace and organization. The response includes + * details of all experiment groups and experiments that match the specified filters. + * + * @throws InternalServerError + */ + default GetExperimentConfigOutput getExperimentConfig(GetExperimentConfigInput input) { + return getExperimentConfig(input, null); + } + + /** + * Retrieves the experiment configuration for a given workspace and organization. The response includes + * details of all experiment groups and experiments that match the specified filters. + * + * @throws InternalServerError + */ + GetExperimentConfigOutput getExperimentConfig(GetExperimentConfigInput input, RequestOverrideConfig overrideConfig); + /** * Retrieves an existing experiment group by its ID. * @@ -1816,12 +1818,12 @@ final class Builder extends Client.Builder { Node.objectNode() ); - private static final HttpBasicAuthTrait httpBasicAuthScheme = new HttpBasicAuthTrait(); - private static final AuthSchemeFactory httpBasicAuthSchemeFactory = new HttpBasicAuthAuthScheme.Factory(); - private static final HttpBearerAuthTrait httpBearerAuthScheme = new HttpBearerAuthTrait(); private static final AuthSchemeFactory httpBearerAuthSchemeFactory = new HttpBearerAuthScheme.Factory(); + private static final HttpBasicAuthTrait httpBasicAuthScheme = new HttpBasicAuthTrait(); + private static final AuthSchemeFactory httpBasicAuthSchemeFactory = new HttpBasicAuthAuthScheme.Factory(); + private Builder() { configBuilder().putSupportedAuthSchemes(httpBasicAuthSchemeFactory.createAuthScheme(httpBasicAuthScheme), httpBearerAuthSchemeFactory.createAuthScheme(httpBearerAuthScheme)); } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java index b52594dd5..a5c489143 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/client/SuperpositionClientImpl.java @@ -80,9 +80,6 @@ import io.juspay.superposition.model.DiscardExperimentInput; import io.juspay.superposition.model.DiscardExperimentOutput; import io.juspay.superposition.model.GetConfig; -import io.juspay.superposition.model.GetConfigFast; -import io.juspay.superposition.model.GetConfigFastInput; -import io.juspay.superposition.model.GetConfigFastOutput; import io.juspay.superposition.model.GetConfigInput; import io.juspay.superposition.model.GetConfigJson; import io.juspay.superposition.model.GetConfigJsonInput; @@ -104,6 +101,9 @@ import io.juspay.superposition.model.GetDimensionInput; import io.juspay.superposition.model.GetDimensionOutput; import io.juspay.superposition.model.GetExperiment; +import io.juspay.superposition.model.GetExperimentConfig; +import io.juspay.superposition.model.GetExperimentConfigInput; +import io.juspay.superposition.model.GetExperimentConfigOutput; import io.juspay.superposition.model.GetExperimentGroup; import io.juspay.superposition.model.GetExperimentGroupInput; import io.juspay.superposition.model.GetExperimentGroupOutput; @@ -272,8 +272,8 @@ @SmithyGenerated final class SuperpositionClientImpl extends Client implements SuperpositionClient { private static final TypeRegistry TYPE_REGISTRY = TypeRegistry.builder() - .putType(AccessDeniedException.$ID, AccessDeniedException.class, AccessDeniedException::builder) .putType(ValidationException.$ID, ValidationException.class, ValidationException::builder) + .putType(AccessDeniedException.$ID, AccessDeniedException.class, AccessDeniedException::builder) .putType(NotAuthorizedException.$ID, NotAuthorizedException.class, NotAuthorizedException::builder) .putType(InternalFailureException.$ID, InternalFailureException.class, InternalFailureException::builder) .putType(UnknownOperationException.$ID, UnknownOperationException.class, UnknownOperationException::builder) @@ -528,15 +528,6 @@ public GetConfigOutput getConfig(GetConfigInput input, RequestOverrideConfig ove } } - @Override - public GetConfigFastOutput getConfigFast(GetConfigFastInput input, RequestOverrideConfig overrideConfig) { - try { - return call(input, GetConfigFast.instance(), overrideConfig).join(); - } catch (CompletionException e) { - throw unwrapAndThrow(e); - } - } - @Override public GetConfigJsonOutput getConfigJson(GetConfigJsonInput input, RequestOverrideConfig overrideConfig) { try { @@ -600,6 +591,15 @@ public GetExperimentOutput getExperiment(GetExperimentInput input, RequestOverri } } + @Override + public GetExperimentConfigOutput getExperimentConfig(GetExperimentConfigInput input, RequestOverrideConfig overrideConfig) { + try { + return call(input, GetExperimentConfig.instance(), overrideConfig).join(); + } catch (CompletionException e) { + throw unwrapAndThrow(e); + } + } + @Override public GetExperimentGroupOutput getExperimentGroup(GetExperimentGroupInput input, RequestOverrideConfig overrideConfig) { try { diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/Config.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/Config.java index 46bf34722..97ac4faf9 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/Config.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/Config.java @@ -19,8 +19,7 @@ public final class Config implements ApiResource { "config", PreludeSchemas.DOCUMENT, "last_modified", SharedSchemas.DATE_TIME); - private static final List $OPERATIONS = List.of(GetConfigFast.$SCHEMA, - GetConfig.$SCHEMA, + private static final List $OPERATIONS = List.of(GetConfig.$SCHEMA, GetResolvedConfig.$SCHEMA, GetResolvedConfigWithIdentifier.$SCHEMA, GetConfigToml.$SCHEMA, diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ExperimentConfig.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ExperimentConfig.java new file mode 100644 index 000000000..73950d6f6 --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ExperimentConfig.java @@ -0,0 +1,59 @@ + +package io.juspay.superposition.model; + +import java.util.List; +import java.util.Map; +import software.amazon.smithy.java.core.schema.ApiResource; +import software.amazon.smithy.java.core.schema.PreludeSchemas; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.utils.SmithyGenerated; + +/** + * Represents a configuration of experiments that can be managed together. + */ +@SmithyGenerated +public final class ExperimentConfig implements ApiResource { + public static final ShapeId $ID = ShapeId.from("io.superposition#ExperimentConfig"); + private static final ExperimentConfig $INSTANCE = new ExperimentConfig(); + private static final Map $IDENTIFIERS = Map.of("workspace_id", PreludeSchemas.STRING, + "org_id", PreludeSchemas.STRING); + private static final Map $PROPERTIES = Map.of("experiment_groups", SharedSchemas.EXPERIMENT_GROUP_LIST, + "experiments", SharedSchemas.EXPERIMENT_LIST, + "last_modified", SharedSchemas.DATE_TIME); + + private static final List $OPERATIONS = List.of(GetExperimentConfig.$SCHEMA); + private static final Schema $SCHEMA = Schema.createResource($ID); + + /** + * Get an instance of this {@code ApiResource}. + * + * @return An instance of this class. + */ + public static ExperimentConfig instance() { + return $INSTANCE; + } + + private ExperimentConfig() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public Map identifiers() { + return $IDENTIFIERS; + } + + @Override + public Map properties() { + return $PROPERTIES; + } + + @Override + public List operations() { + return $OPERATIONS; + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ExperimentGroup.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ExperimentGroup.java index 04ffb1bca..8be02229c 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ExperimentGroup.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ExperimentGroup.java @@ -34,6 +34,8 @@ public final class ExperimentGroup implements ApiResource { Map.entry("context", SharedSchemas.CONDITION), Map.entry("member_experiment_ids", SharedSchemas.STRING_LIST)); + private static final List$COLLECTION_OPERATIONS = List.of(AddMembersToGroup.$SCHEMA, + RemoveMembersFromGroup.$SCHEMA); private static final List $OPERATIONS = List.of(AddMembersToGroup.$SCHEMA, RemoveMembersFromGroup.$SCHEMA); private static final Schema $SCHEMA = Schema.createResource($ID); @@ -85,10 +87,9 @@ public Schema delete() { } @Override - public Schema list() { - return ListExperimentGroups.$SCHEMA; + public List collectionOperations() { + return $COLLECTION_OPERATIONS; } - @Override public List operations() { return $OPERATIONS; diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/Experiments.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/Experiments.java index 05445da76..7d275572d 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/Experiments.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/Experiments.java @@ -87,11 +87,6 @@ public Schema read() { return GetExperiment.$SCHEMA; } - @Override - public Schema list() { - return ListExperiment.$SCHEMA; - } - @Override public List collectionOperations() { return $COLLECTION_OPERATIONS; diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastInput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastInput.java deleted file mode 100644 index 12e138ce6..000000000 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastInput.java +++ /dev/null @@ -1,207 +0,0 @@ - -package io.juspay.superposition.model; - -import java.util.Objects; -import software.amazon.smithy.java.core.schema.PreludeSchemas; -import software.amazon.smithy.java.core.schema.PresenceTracker; -import software.amazon.smithy.java.core.schema.Schema; -import software.amazon.smithy.java.core.schema.SchemaUtils; -import software.amazon.smithy.java.core.schema.SerializableStruct; -import software.amazon.smithy.java.core.schema.ShapeBuilder; -import software.amazon.smithy.java.core.serde.ShapeDeserializer; -import software.amazon.smithy.java.core.serde.ShapeSerializer; -import software.amazon.smithy.java.core.serde.ToStringSerializer; -import software.amazon.smithy.model.shapes.ShapeId; -import software.amazon.smithy.model.traits.HttpHeaderTrait; -import software.amazon.smithy.model.traits.RequiredTrait; -import software.amazon.smithy.utils.SmithyGenerated; - -@SmithyGenerated -public final class GetConfigFastInput implements SerializableStruct { - public static final ShapeId $ID = ShapeId.from("io.superposition#GetConfigFastInput"); - - public static final Schema $SCHEMA = Schema.structureBuilder($ID) - .putMember("workspace_id", PreludeSchemas.STRING, - new HttpHeaderTrait("x-workspace"), - new RequiredTrait()) - .putMember("org_id", PreludeSchemas.STRING, - new HttpHeaderTrait("x-org-id"), - new RequiredTrait()) - .build(); - - private static final Schema $SCHEMA_WORKSPACE_ID = $SCHEMA.member("workspace_id"); - private static final Schema $SCHEMA_ORG_ID = $SCHEMA.member("org_id"); - - private final transient String workspaceId; - private final transient String orgId; - - private GetConfigFastInput(Builder builder) { - this.workspaceId = builder.workspaceId; - this.orgId = builder.orgId; - } - - public String workspaceId() { - return workspaceId; - } - - public String orgId() { - return orgId; - } - - @Override - public String toString() { - return ToStringSerializer.serialize(this); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; - } - GetConfigFastInput that = (GetConfigFastInput) other; - return Objects.equals(this.workspaceId, that.workspaceId) - && Objects.equals(this.orgId, that.orgId); - } - - @Override - public int hashCode() { - return Objects.hash(workspaceId, orgId); - } - - @Override - public Schema schema() { - return $SCHEMA; - } - - @Override - public void serializeMembers(ShapeSerializer serializer) { - serializer.writeString($SCHEMA_WORKSPACE_ID, workspaceId); - serializer.writeString($SCHEMA_ORG_ID, orgId); - } - - @Override - @SuppressWarnings("unchecked") - public T getMemberValue(Schema member) { - return switch (member.memberIndex()) { - case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, workspaceId); - case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, orgId); - default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); - }; - } - - /** - * Create a new builder containing all the current property values of this object. - * - *

Note: This method performs only a shallow copy of the original properties. - * - * @return a builder for {@link GetConfigFastInput}. - */ - public Builder toBuilder() { - var builder = new Builder(); - builder.workspaceId(this.workspaceId); - builder.orgId(this.orgId); - return builder; - } - - /** - * @return returns a new Builder. - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Builder for {@link GetConfigFastInput}. - */ - public static final class Builder implements ShapeBuilder { - private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); - private String workspaceId; - private String orgId; - - private Builder() {} - - @Override - public Schema schema() { - return $SCHEMA; - } - - /** - *

Required - * @return this builder. - */ - public Builder workspaceId(String workspaceId) { - this.workspaceId = Objects.requireNonNull(workspaceId, "workspaceId cannot be null"); - tracker.setMember($SCHEMA_WORKSPACE_ID); - return this; - } - - /** - *

Required - * @return this builder. - */ - public Builder orgId(String orgId) { - this.orgId = Objects.requireNonNull(orgId, "orgId cannot be null"); - tracker.setMember($SCHEMA_ORG_ID); - return this; - } - - @Override - public GetConfigFastInput build() { - tracker.validate(); - return new GetConfigFastInput(this); - } - - @Override - @SuppressWarnings("unchecked") - public void setMemberValue(Schema member, Object value) { - switch (member.memberIndex()) { - case 0 -> workspaceId((String) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, value)); - case 1 -> orgId((String) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, value)); - default -> ShapeBuilder.super.setMemberValue(member, value); - } - } - - @Override - public ShapeBuilder errorCorrection() { - if (tracker.allSet()) { - return this; - } - if (!tracker.checkMember($SCHEMA_WORKSPACE_ID)) { - workspaceId(""); - } - if (!tracker.checkMember($SCHEMA_ORG_ID)) { - orgId(""); - } - return this; - } - - @Override - public Builder deserialize(ShapeDeserializer decoder) { - decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE); - return this; - } - - @Override - public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) { - decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE); - return this; - } - - private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer { - private static final $InnerDeserializer INSTANCE = new $InnerDeserializer(); - - @Override - public void accept(Builder builder, Schema member, ShapeDeserializer de) { - switch (member.memberIndex()) { - case 0 -> builder.workspaceId(de.readString(member)); - case 1 -> builder.orgId(de.readString(member)); - default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); - } - } - } - } -} - diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastOutput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastOutput.java deleted file mode 100644 index b11d1ad55..000000000 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFastOutput.java +++ /dev/null @@ -1,242 +0,0 @@ - -package io.juspay.superposition.model; - -import java.time.Instant; -import java.util.Objects; -import software.amazon.smithy.java.core.schema.PreludeSchemas; -import software.amazon.smithy.java.core.schema.Schema; -import software.amazon.smithy.java.core.schema.SchemaUtils; -import software.amazon.smithy.java.core.schema.SerializableStruct; -import software.amazon.smithy.java.core.schema.ShapeBuilder; -import software.amazon.smithy.java.core.serde.ShapeDeserializer; -import software.amazon.smithy.java.core.serde.ShapeSerializer; -import software.amazon.smithy.java.core.serde.ToStringSerializer; -import software.amazon.smithy.java.core.serde.document.Document; -import software.amazon.smithy.model.shapes.ShapeId; -import software.amazon.smithy.model.traits.HttpHeaderTrait; -import software.amazon.smithy.model.traits.HttpPayloadTrait; -import software.amazon.smithy.utils.SmithyGenerated; - -@SmithyGenerated -public final class GetConfigFastOutput implements SerializableStruct { - public static final ShapeId $ID = ShapeId.from("io.superposition#GetConfigFastOutput"); - - public static final Schema $SCHEMA = Schema.structureBuilder($ID) - .putMember("config", PreludeSchemas.DOCUMENT, - new HttpPayloadTrait()) - .putMember("version", PreludeSchemas.STRING, - new HttpHeaderTrait("x-config-version")) - .putMember("last_modified", SharedSchemas.DATE_TIME, - new HttpHeaderTrait("last-modified")) - .putMember("audit_id", PreludeSchemas.STRING, - new HttpHeaderTrait("x-audit-id")) - .build(); - - private static final Schema $SCHEMA_CONFIG = $SCHEMA.member("config"); - private static final Schema $SCHEMA_VERSION = $SCHEMA.member("version"); - private static final Schema $SCHEMA_LAST_MODIFIED = $SCHEMA.member("last_modified"); - private static final Schema $SCHEMA_AUDIT_ID = $SCHEMA.member("audit_id"); - - private final transient Document config; - private final transient String version; - private final transient Instant lastModified; - private final transient String auditId; - - private GetConfigFastOutput(Builder builder) { - this.config = builder.config; - this.version = builder.version; - this.lastModified = builder.lastModified; - this.auditId = builder.auditId; - } - - public Document config() { - return config; - } - - public String version() { - return version; - } - - public Instant lastModified() { - return lastModified; - } - - public String auditId() { - return auditId; - } - - @Override - public String toString() { - return ToStringSerializer.serialize(this); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; - } - GetConfigFastOutput that = (GetConfigFastOutput) other; - return Objects.equals(this.config, that.config) - && Objects.equals(this.version, that.version) - && Objects.equals(this.lastModified, that.lastModified) - && Objects.equals(this.auditId, that.auditId); - } - - @Override - public int hashCode() { - return Objects.hash(config, version, lastModified, auditId); - } - - @Override - public Schema schema() { - return $SCHEMA; - } - - @Override - public void serializeMembers(ShapeSerializer serializer) { - if (config != null) { - serializer.writeDocument($SCHEMA_CONFIG, config); - } - if (version != null) { - serializer.writeString($SCHEMA_VERSION, version); - } - if (lastModified != null) { - serializer.writeTimestamp($SCHEMA_LAST_MODIFIED, lastModified); - } - if (auditId != null) { - serializer.writeString($SCHEMA_AUDIT_ID, auditId); - } - } - - @Override - @SuppressWarnings("unchecked") - public T getMemberValue(Schema member) { - return switch (member.memberIndex()) { - case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONFIG, member, config); - case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_VERSION, member, version); - case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED, member, lastModified); - case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_AUDIT_ID, member, auditId); - default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); - }; - } - - /** - * Create a new builder containing all the current property values of this object. - * - *

Note: This method performs only a shallow copy of the original properties. - * - * @return a builder for {@link GetConfigFastOutput}. - */ - public Builder toBuilder() { - var builder = new Builder(); - builder.config(this.config); - builder.version(this.version); - builder.lastModified(this.lastModified); - builder.auditId(this.auditId); - return builder; - } - - /** - * @return returns a new Builder. - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Builder for {@link GetConfigFastOutput}. - */ - public static final class Builder implements ShapeBuilder { - private Document config; - private String version; - private Instant lastModified; - private String auditId; - - private Builder() {} - - @Override - public Schema schema() { - return $SCHEMA; - } - - /** - * @return this builder. - */ - public Builder config(Document config) { - this.config = config; - return this; - } - - /** - * @return this builder. - */ - public Builder version(String version) { - this.version = version; - return this; - } - - /** - * @return this builder. - */ - public Builder lastModified(Instant lastModified) { - this.lastModified = lastModified; - return this; - } - - /** - * @return this builder. - */ - public Builder auditId(String auditId) { - this.auditId = auditId; - return this; - } - - @Override - public GetConfigFastOutput build() { - return new GetConfigFastOutput(this); - } - - @Override - @SuppressWarnings("unchecked") - public void setMemberValue(Schema member, Object value) { - switch (member.memberIndex()) { - case 0 -> config((Document) SchemaUtils.validateSameMember($SCHEMA_CONFIG, member, value)); - case 1 -> version((String) SchemaUtils.validateSameMember($SCHEMA_VERSION, member, value)); - case 2 -> lastModified((Instant) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED, member, value)); - case 3 -> auditId((String) SchemaUtils.validateSameMember($SCHEMA_AUDIT_ID, member, value)); - default -> ShapeBuilder.super.setMemberValue(member, value); - } - } - - @Override - public Builder deserialize(ShapeDeserializer decoder) { - decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE); - return this; - } - - @Override - public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) { - decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE); - return this; - } - - private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer { - private static final $InnerDeserializer INSTANCE = new $InnerDeserializer(); - - @Override - public void accept(Builder builder, Schema member, ShapeDeserializer de) { - switch (member.memberIndex()) { - case 0 -> builder.config(de.readDocument()); - case 1 -> builder.version(de.readString(member)); - case 2 -> builder.lastModified(de.readTimestamp(member)); - case 3 -> builder.auditId(de.readString(member)); - default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); - } - } - } - } -} - diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigInput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigInput.java index 59431c88d..77b01791b 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigInput.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigInput.java @@ -1,6 +1,7 @@ package io.juspay.superposition.model; +import java.time.Instant; import java.util.Collections; import java.util.List; import java.util.Map; @@ -36,6 +37,8 @@ public final class GetConfigInput implements SerializableStruct { new HttpQueryTrait("prefix")) .putMember("version", PreludeSchemas.STRING, new HttpQueryTrait("version")) + .putMember("if_modified_since", SharedSchemas.DATE_TIME, + new HttpHeaderTrait("if-modified-since")) .putMember("context", SharedSchemas.CONTEXT_MAP) .build(); @@ -43,12 +46,14 @@ public final class GetConfigInput implements SerializableStruct { private static final Schema $SCHEMA_ORG_ID = $SCHEMA.member("org_id"); private static final Schema $SCHEMA_PREFIX = $SCHEMA.member("prefix"); private static final Schema $SCHEMA_VERSION = $SCHEMA.member("version"); + private static final Schema $SCHEMA_IF_MODIFIED_SINCE = $SCHEMA.member("if_modified_since"); private static final Schema $SCHEMA_CONTEXT = $SCHEMA.member("context"); private final transient String workspaceId; private final transient String orgId; private final transient List prefix; private final transient String version; + private final transient Instant ifModifiedSince; private final transient Map context; private GetConfigInput(Builder builder) { @@ -56,6 +61,7 @@ private GetConfigInput(Builder builder) { this.orgId = builder.orgId; this.prefix = builder.prefix == null ? null : Collections.unmodifiableList(builder.prefix); this.version = builder.version; + this.ifModifiedSince = builder.ifModifiedSince; this.context = builder.context == null ? null : Collections.unmodifiableMap(builder.context); } @@ -82,6 +88,15 @@ public String version() { return version; } + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking + * the response code of the http response. This is required to make sure that clients can cache the + * response and avoid unnecessary calls when there are no updates. + */ + public Instant ifModifiedSince() { + return ifModifiedSince; + } + public Map context() { if (context == null) { return Collections.emptyMap(); @@ -111,12 +126,13 @@ public boolean equals(Object other) { && Objects.equals(this.orgId, that.orgId) && Objects.equals(this.prefix, that.prefix) && Objects.equals(this.version, that.version) + && Objects.equals(this.ifModifiedSince, that.ifModifiedSince) && Objects.equals(this.context, that.context); } @Override public int hashCode() { - return Objects.hash(workspaceId, orgId, prefix, version, context); + return Objects.hash(workspaceId, orgId, prefix, version, ifModifiedSince, context); } @Override @@ -134,6 +150,9 @@ public void serializeMembers(ShapeSerializer serializer) { if (version != null) { serializer.writeString($SCHEMA_VERSION, version); } + if (ifModifiedSince != null) { + serializer.writeTimestamp($SCHEMA_IF_MODIFIED_SINCE, ifModifiedSince); + } if (context != null) { serializer.writeMap($SCHEMA_CONTEXT, context, context.size(), SharedSerde.ContextMapSerializer.INSTANCE); } @@ -147,7 +166,8 @@ public T getMemberValue(Schema member) { case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, orgId); case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_PREFIX, member, prefix); case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_VERSION, member, version); - case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONTEXT, member, context); + case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, ifModifiedSince); + case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONTEXT, member, context); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -165,6 +185,7 @@ public Builder toBuilder() { builder.orgId(this.orgId); builder.prefix(this.prefix); builder.version(this.version); + builder.ifModifiedSince(this.ifModifiedSince); builder.context(this.context); return builder; } @@ -185,6 +206,7 @@ public static final class Builder implements ShapeBuilder { private String orgId; private List prefix; private String version; + private Instant ifModifiedSince; private Map context; private Builder() {} @@ -230,6 +252,18 @@ public Builder version(String version) { return this; } + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking + * the response code of the http response. This is required to make sure that clients can cache the + * response and avoid unnecessary calls when there are no updates. + * + * @return this builder. + */ + public Builder ifModifiedSince(Instant ifModifiedSince) { + this.ifModifiedSince = ifModifiedSince; + return this; + } + /** * @return this builder. */ @@ -252,7 +286,8 @@ public void setMemberValue(Schema member, Object value) { case 1 -> orgId((String) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, value)); case 2 -> prefix((List) SchemaUtils.validateSameMember($SCHEMA_PREFIX, member, value)); case 3 -> version((String) SchemaUtils.validateSameMember($SCHEMA_VERSION, member, value)); - case 4 -> context((Map) SchemaUtils.validateSameMember($SCHEMA_CONTEXT, member, value)); + case 4 -> ifModifiedSince((Instant) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, value)); + case 5 -> context((Map) SchemaUtils.validateSameMember($SCHEMA_CONTEXT, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -293,7 +328,8 @@ public void accept(Builder builder, Schema member, ShapeDeserializer de) { case 1 -> builder.orgId(de.readString(member)); case 2 -> builder.prefix(SharedSerde.deserializeStringList(member, de)); case 3 -> builder.version(de.readString(member)); - case 4 -> builder.context(SharedSerde.deserializeContextMap(member, de)); + case 4 -> builder.ifModifiedSince(de.readTimestamp(member)); + case 5 -> builder.context(SharedSerde.deserializeContextMap(member, de)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigJson.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigJson.java index 520be8f76..786558e3c 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigJson.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigJson.java @@ -24,7 +24,7 @@ public final class GetConfigJson implements ApiOperation T getMemberValue(Schema member) { return switch (member.memberIndex()) { case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, workspaceId); case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, orgId); + case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, ifModifiedSince); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -103,6 +123,7 @@ public Builder toBuilder() { var builder = new Builder(); builder.workspaceId(this.workspaceId); builder.orgId(this.orgId); + builder.ifModifiedSince(this.ifModifiedSince); return builder; } @@ -120,6 +141,7 @@ public static final class Builder implements ShapeBuilder { private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); private String workspaceId; private String orgId; + private Instant ifModifiedSince; private Builder() {} @@ -148,6 +170,18 @@ public Builder orgId(String orgId) { return this; } + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking + * the response code of the http response. This is required to make sure that clients can cache the + * response and avoid unnecessary calls when there are no updates. + * + * @return this builder. + */ + public Builder ifModifiedSince(Instant ifModifiedSince) { + this.ifModifiedSince = ifModifiedSince; + return this; + } + @Override public GetConfigJsonInput build() { tracker.validate(); @@ -160,6 +194,7 @@ public void setMemberValue(Schema member, Object value) { switch (member.memberIndex()) { case 0 -> workspaceId((String) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, value)); case 1 -> orgId((String) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, value)); + case 2 -> ifModifiedSince((Instant) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -198,6 +233,7 @@ public void accept(Builder builder, Schema member, ShapeDeserializer de) { switch (member.memberIndex()) { case 0 -> builder.workspaceId(de.readString(member)); case 1 -> builder.orgId(de.readString(member)); + case 2 -> builder.ifModifiedSince(de.readTimestamp(member)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigOutput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigOutput.java index a1e09d95a..d8886c549 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigOutput.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigOutput.java @@ -40,8 +40,6 @@ public final class GetConfigOutput implements SerializableStruct { .putMember("last_modified", SharedSchemas.DATE_TIME, new HttpHeaderTrait("last-modified"), new RequiredTrait()) - .putMember("audit_id", PreludeSchemas.STRING, - new HttpHeaderTrait("x-audit-id")) .build(); private static final Schema $SCHEMA_CONTEXTS = $SCHEMA.member("contexts"); @@ -50,7 +48,6 @@ public final class GetConfigOutput implements SerializableStruct { private static final Schema $SCHEMA_DIMENSIONS = $SCHEMA.member("dimensions"); private static final Schema $SCHEMA_VERSION = $SCHEMA.member("version"); private static final Schema $SCHEMA_LAST_MODIFIED = $SCHEMA.member("last_modified"); - private static final Schema $SCHEMA_AUDIT_ID = $SCHEMA.member("audit_id"); private final transient List contexts; private final transient Map> overrides; @@ -58,7 +55,6 @@ public final class GetConfigOutput implements SerializableStruct { private final transient Map dimensions; private final transient String version; private final transient Instant lastModified; - private final transient String auditId; private GetConfigOutput(Builder builder) { this.contexts = Collections.unmodifiableList(builder.contexts); @@ -67,7 +63,6 @@ private GetConfigOutput(Builder builder) { this.dimensions = Collections.unmodifiableMap(builder.dimensions); this.version = builder.version; this.lastModified = builder.lastModified; - this.auditId = builder.auditId; } public List contexts() { @@ -110,10 +105,6 @@ public Instant lastModified() { return lastModified; } - public String auditId() { - return auditId; - } - @Override public String toString() { return ToStringSerializer.serialize(this); @@ -133,13 +124,12 @@ public boolean equals(Object other) { && Objects.equals(this.defaultConfigs, that.defaultConfigs) && Objects.equals(this.dimensions, that.dimensions) && Objects.equals(this.version, that.version) - && Objects.equals(this.lastModified, that.lastModified) - && Objects.equals(this.auditId, that.auditId); + && Objects.equals(this.lastModified, that.lastModified); } @Override public int hashCode() { - return Objects.hash(contexts, overrides, defaultConfigs, dimensions, version, lastModified, auditId); + return Objects.hash(contexts, overrides, defaultConfigs, dimensions, version, lastModified); } @Override @@ -155,9 +145,6 @@ public void serializeMembers(ShapeSerializer serializer) { serializer.writeMap($SCHEMA_DIMENSIONS, dimensions, dimensions.size(), SharedSerde.DimensionDataSerializer.INSTANCE); serializer.writeString($SCHEMA_VERSION, version); serializer.writeTimestamp($SCHEMA_LAST_MODIFIED, lastModified); - if (auditId != null) { - serializer.writeString($SCHEMA_AUDIT_ID, auditId); - } } @Override @@ -170,7 +157,6 @@ public T getMemberValue(Schema member) { case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_DIMENSIONS, member, dimensions); case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_VERSION, member, version); case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED, member, lastModified); - case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_AUDIT_ID, member, auditId); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -190,7 +176,6 @@ public Builder toBuilder() { builder.dimensions(this.dimensions); builder.version(this.version); builder.lastModified(this.lastModified); - builder.auditId(this.auditId); return builder; } @@ -212,7 +197,6 @@ public static final class Builder implements ShapeBuilder { private Map dimensions; private String version; private Instant lastModified; - private String auditId; private Builder() {} @@ -281,14 +265,6 @@ public Builder lastModified(Instant lastModified) { return this; } - /** - * @return this builder. - */ - public Builder auditId(String auditId) { - this.auditId = auditId; - return this; - } - @Override public GetConfigOutput build() { tracker.validate(); @@ -305,7 +281,6 @@ public void setMemberValue(Schema member, Object value) { case 3 -> dimensions((Map) SchemaUtils.validateSameMember($SCHEMA_DIMENSIONS, member, value)); case 4 -> version((String) SchemaUtils.validateSameMember($SCHEMA_VERSION, member, value)); case 5 -> lastModified((Instant) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED, member, value)); - case 6 -> auditId((String) SchemaUtils.validateSameMember($SCHEMA_AUDIT_ID, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -360,7 +335,6 @@ public void accept(Builder builder, Schema member, ShapeDeserializer de) { case 3 -> builder.dimensions(SharedSerde.deserializeDimensionData(member, de)); case 4 -> builder.version(de.readString(member)); case 5 -> builder.lastModified(de.readTimestamp(member)); - case 6 -> builder.auditId(de.readString(member)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigToml.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigToml.java index b2756581d..794a48617 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigToml.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigToml.java @@ -24,7 +24,7 @@ public final class GetConfigToml implements ApiOperation T getMemberValue(Schema member) { return switch (member.memberIndex()) { case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, workspaceId); case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, orgId); + case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, ifModifiedSince); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -103,6 +123,7 @@ public Builder toBuilder() { var builder = new Builder(); builder.workspaceId(this.workspaceId); builder.orgId(this.orgId); + builder.ifModifiedSince(this.ifModifiedSince); return builder; } @@ -120,6 +141,7 @@ public static final class Builder implements ShapeBuilder { private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); private String workspaceId; private String orgId; + private Instant ifModifiedSince; private Builder() {} @@ -148,6 +170,18 @@ public Builder orgId(String orgId) { return this; } + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking + * the response code of the http response. This is required to make sure that clients can cache the + * response and avoid unnecessary calls when there are no updates. + * + * @return this builder. + */ + public Builder ifModifiedSince(Instant ifModifiedSince) { + this.ifModifiedSince = ifModifiedSince; + return this; + } + @Override public GetConfigTomlInput build() { tracker.validate(); @@ -160,6 +194,7 @@ public void setMemberValue(Schema member, Object value) { switch (member.memberIndex()) { case 0 -> workspaceId((String) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, value)); case 1 -> orgId((String) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, value)); + case 2 -> ifModifiedSince((Instant) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -198,6 +233,7 @@ public void accept(Builder builder, Schema member, ShapeDeserializer de) { switch (member.memberIndex()) { case 0 -> builder.workspaceId(de.readString(member)); case 1 -> builder.orgId(de.readString(member)); + case 2 -> builder.ifModifiedSince(de.readTimestamp(member)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFast.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetExperimentConfig.java similarity index 66% rename from clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFast.java rename to clients/java/sdk/src/main/java/io/juspay/superposition/model/GetExperimentConfig.java index d9d36aaf3..cdfdb686c 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetConfigFast.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetExperimentConfig.java @@ -13,16 +13,17 @@ import software.amazon.smithy.utils.SmithyGenerated; /** - * Retrieves the latest config with no processing for high-performance access. + * Retrieves the experiment configuration for a given workspace and organization. The response includes + * details of all experiment groups and experiments that match the specified filters. */ @SmithyGenerated -public final class GetConfigFast implements ApiOperation { - public static final ShapeId $ID = ShapeId.from("io.superposition#GetConfigFast"); +public final class GetExperimentConfig implements ApiOperation { + public static final ShapeId $ID = ShapeId.from("io.superposition#GetExperimentConfig"); - private static final GetConfigFast $INSTANCE = new GetConfigFast(); + private static final GetExperimentConfig $INSTANCE = new GetExperimentConfig(); static final Schema $SCHEMA = Schema.createOperation($ID, - HttpTrait.builder().method("GET").code(200).uri(UriPattern.parse("/config/fast")).build()); + HttpTrait.builder().method("POST").code(200).uri(UriPattern.parse("/experiment-config")).build()); private static final TypeRegistry TYPE_REGISTRY = TypeRegistry.builder() .putType(InternalServerError.$ID, InternalServerError.class, InternalServerError::builder) @@ -35,20 +36,20 @@ public final class GetConfigFast implements ApiOperation inputBuilder() { - return GetConfigFastInput.builder(); + public ShapeBuilder inputBuilder() { + return GetExperimentConfigInput.builder(); } @Override - public ShapeBuilder outputBuilder() { - return GetConfigFastOutput.builder(); + public ShapeBuilder outputBuilder() { + return GetExperimentConfigOutput.builder(); } @Override @@ -58,12 +59,12 @@ public Schema schema() { @Override public Schema inputSchema() { - return GetConfigFastInput.$SCHEMA; + return GetExperimentConfigInput.$SCHEMA; } @Override public Schema outputSchema() { - return GetConfigFastOutput.$SCHEMA; + return GetExperimentConfigOutput.$SCHEMA; } @Override @@ -93,7 +94,7 @@ public Schema idempotencyTokenMember() { @Override public ApiResource boundResource() { - return Config.instance(); + return ExperimentConfig.instance(); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetExperimentConfigInput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetExperimentConfigInput.java new file mode 100644 index 000000000..a9a14f6e6 --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetExperimentConfigInput.java @@ -0,0 +1,339 @@ + +package io.juspay.superposition.model; + +import java.time.Instant; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import software.amazon.smithy.java.core.schema.PreludeSchemas; +import software.amazon.smithy.java.core.schema.PresenceTracker; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SchemaUtils; +import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.java.core.serde.document.Document; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.HttpHeaderTrait; +import software.amazon.smithy.model.traits.HttpQueryTrait; +import software.amazon.smithy.model.traits.RequiredTrait; +import software.amazon.smithy.utils.SmithyGenerated; + +@SmithyGenerated +public final class GetExperimentConfigInput implements SerializableStruct { + public static final ShapeId $ID = ShapeId.from("io.superposition#GetExperimentConfigInput"); + + public static final Schema $SCHEMA = Schema.structureBuilder($ID) + .putMember("workspace_id", PreludeSchemas.STRING, + new HttpHeaderTrait("x-workspace"), + new RequiredTrait()) + .putMember("org_id", PreludeSchemas.STRING, + new HttpHeaderTrait("x-org-id"), + new RequiredTrait()) + .putMember("if_modified_since", SharedSchemas.DATE_TIME, + new HttpHeaderTrait("if-modified-since")) + .putMember("prefix", SharedSchemas.STRING_LIST, + new HttpQueryTrait("prefix")) + .putMember("context", SharedSchemas.CONTEXT_MAP) + .putMember("dimension_match_strategy", DimensionMatchStrategy.$SCHEMA, + new HttpQueryTrait("dimension_match_strategy")) + .build(); + + private static final Schema $SCHEMA_WORKSPACE_ID = $SCHEMA.member("workspace_id"); + private static final Schema $SCHEMA_ORG_ID = $SCHEMA.member("org_id"); + private static final Schema $SCHEMA_IF_MODIFIED_SINCE = $SCHEMA.member("if_modified_since"); + private static final Schema $SCHEMA_PREFIX = $SCHEMA.member("prefix"); + private static final Schema $SCHEMA_CONTEXT = $SCHEMA.member("context"); + private static final Schema $SCHEMA_DIMENSION_MATCH_STRATEGY = $SCHEMA.member("dimension_match_strategy"); + + private final transient String workspaceId; + private final transient String orgId; + private final transient Instant ifModifiedSince; + private final transient List prefix; + private final transient Map context; + private final transient DimensionMatchStrategy dimensionMatchStrategy; + + private GetExperimentConfigInput(Builder builder) { + this.workspaceId = builder.workspaceId; + this.orgId = builder.orgId; + this.ifModifiedSince = builder.ifModifiedSince; + this.prefix = builder.prefix == null ? null : Collections.unmodifiableList(builder.prefix); + this.context = builder.context == null ? null : Collections.unmodifiableMap(builder.context); + this.dimensionMatchStrategy = builder.dimensionMatchStrategy; + } + + public String workspaceId() { + return workspaceId; + } + + public String orgId() { + return orgId; + } + + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking + * the response code of the http response. This is required to make sure that clients can cache the + * response and avoid unnecessary calls when there are no updates. + */ + public Instant ifModifiedSince() { + return ifModifiedSince; + } + + public List prefix() { + if (prefix == null) { + return Collections.emptyList(); + } + return prefix; + } + + public boolean hasPrefix() { + return prefix != null; + } + + public Map context() { + if (context == null) { + return Collections.emptyMap(); + } + return context; + } + + public boolean hasContext() { + return context != null; + } + + public DimensionMatchStrategy dimensionMatchStrategy() { + return dimensionMatchStrategy; + } + + @Override + public String toString() { + return ToStringSerializer.serialize(this); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + GetExperimentConfigInput that = (GetExperimentConfigInput) other; + return Objects.equals(this.workspaceId, that.workspaceId) + && Objects.equals(this.orgId, that.orgId) + && Objects.equals(this.ifModifiedSince, that.ifModifiedSince) + && Objects.equals(this.prefix, that.prefix) + && Objects.equals(this.context, that.context) + && Objects.equals(this.dimensionMatchStrategy, that.dimensionMatchStrategy); + } + + @Override + public int hashCode() { + return Objects.hash(workspaceId, orgId, ifModifiedSince, prefix, context, dimensionMatchStrategy); + } + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public void serializeMembers(ShapeSerializer serializer) { + serializer.writeString($SCHEMA_WORKSPACE_ID, workspaceId); + serializer.writeString($SCHEMA_ORG_ID, orgId); + if (ifModifiedSince != null) { + serializer.writeTimestamp($SCHEMA_IF_MODIFIED_SINCE, ifModifiedSince); + } + if (prefix != null) { + serializer.writeList($SCHEMA_PREFIX, prefix, prefix.size(), SharedSerde.StringListSerializer.INSTANCE); + } + if (context != null) { + serializer.writeMap($SCHEMA_CONTEXT, context, context.size(), SharedSerde.ContextMapSerializer.INSTANCE); + } + if (dimensionMatchStrategy != null) { + serializer.writeString($SCHEMA_DIMENSION_MATCH_STRATEGY, dimensionMatchStrategy.value()); + } + } + + @Override + @SuppressWarnings("unchecked") + public T getMemberValue(Schema member) { + return switch (member.memberIndex()) { + case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, workspaceId); + case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, orgId); + case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, ifModifiedSince); + case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_PREFIX, member, prefix); + case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONTEXT, member, context); + case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_DIMENSION_MATCH_STRATEGY, member, dimensionMatchStrategy); + default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); + }; + } + + /** + * Create a new builder containing all the current property values of this object. + * + *

Note: This method performs only a shallow copy of the original properties. + * + * @return a builder for {@link GetExperimentConfigInput}. + */ + public Builder toBuilder() { + var builder = new Builder(); + builder.workspaceId(this.workspaceId); + builder.orgId(this.orgId); + builder.ifModifiedSince(this.ifModifiedSince); + builder.prefix(this.prefix); + builder.context(this.context); + builder.dimensionMatchStrategy(this.dimensionMatchStrategy); + return builder; + } + + /** + * @return returns a new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link GetExperimentConfigInput}. + */ + public static final class Builder implements ShapeBuilder { + private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); + private String workspaceId; + private String orgId; + private Instant ifModifiedSince; + private List prefix; + private Map context; + private DimensionMatchStrategy dimensionMatchStrategy; + + private Builder() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + /** + *

Required + * @return this builder. + */ + public Builder workspaceId(String workspaceId) { + this.workspaceId = Objects.requireNonNull(workspaceId, "workspaceId cannot be null"); + tracker.setMember($SCHEMA_WORKSPACE_ID); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder orgId(String orgId) { + this.orgId = Objects.requireNonNull(orgId, "orgId cannot be null"); + tracker.setMember($SCHEMA_ORG_ID); + return this; + } + + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking + * the response code of the http response. This is required to make sure that clients can cache the + * response and avoid unnecessary calls when there are no updates. + * + * @return this builder. + */ + public Builder ifModifiedSince(Instant ifModifiedSince) { + this.ifModifiedSince = ifModifiedSince; + return this; + } + + /** + * @return this builder. + */ + public Builder prefix(List prefix) { + this.prefix = prefix; + return this; + } + + /** + * @return this builder. + */ + public Builder context(Map context) { + this.context = context; + return this; + } + + /** + * @return this builder. + */ + public Builder dimensionMatchStrategy(DimensionMatchStrategy dimensionMatchStrategy) { + this.dimensionMatchStrategy = dimensionMatchStrategy; + return this; + } + + @Override + public GetExperimentConfigInput build() { + tracker.validate(); + return new GetExperimentConfigInput(this); + } + + @Override + @SuppressWarnings("unchecked") + public void setMemberValue(Schema member, Object value) { + switch (member.memberIndex()) { + case 0 -> workspaceId((String) SchemaUtils.validateSameMember($SCHEMA_WORKSPACE_ID, member, value)); + case 1 -> orgId((String) SchemaUtils.validateSameMember($SCHEMA_ORG_ID, member, value)); + case 2 -> ifModifiedSince((Instant) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, value)); + case 3 -> prefix((List) SchemaUtils.validateSameMember($SCHEMA_PREFIX, member, value)); + case 4 -> context((Map) SchemaUtils.validateSameMember($SCHEMA_CONTEXT, member, value)); + case 5 -> dimensionMatchStrategy((DimensionMatchStrategy) SchemaUtils.validateSameMember($SCHEMA_DIMENSION_MATCH_STRATEGY, member, value)); + default -> ShapeBuilder.super.setMemberValue(member, value); + } + } + + @Override + public ShapeBuilder errorCorrection() { + if (tracker.allSet()) { + return this; + } + if (!tracker.checkMember($SCHEMA_WORKSPACE_ID)) { + workspaceId(""); + } + if (!tracker.checkMember($SCHEMA_ORG_ID)) { + orgId(""); + } + return this; + } + + @Override + public Builder deserialize(ShapeDeserializer decoder) { + decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE); + return this; + } + + @Override + public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) { + decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE); + return this; + } + + private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer { + private static final $InnerDeserializer INSTANCE = new $InnerDeserializer(); + + @Override + public void accept(Builder builder, Schema member, ShapeDeserializer de) { + switch (member.memberIndex()) { + case 0 -> builder.workspaceId(de.readString(member)); + case 1 -> builder.orgId(de.readString(member)); + case 2 -> builder.ifModifiedSince(de.readTimestamp(member)); + case 3 -> builder.prefix(SharedSerde.deserializeStringList(member, de)); + case 4 -> builder.context(SharedSerde.deserializeContextMap(member, de)); + case 5 -> builder.dimensionMatchStrategy(DimensionMatchStrategy.builder().deserializeMember(de, member).build()); + default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); + } + } + } + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetExperimentConfigOutput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetExperimentConfigOutput.java new file mode 100644 index 000000000..172f2179b --- /dev/null +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/GetExperimentConfigOutput.java @@ -0,0 +1,245 @@ + +package io.juspay.superposition.model; + +import java.time.Instant; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import software.amazon.smithy.java.core.schema.PresenceTracker; +import software.amazon.smithy.java.core.schema.Schema; +import software.amazon.smithy.java.core.schema.SchemaUtils; +import software.amazon.smithy.java.core.schema.SerializableStruct; +import software.amazon.smithy.java.core.schema.ShapeBuilder; +import software.amazon.smithy.java.core.serde.ShapeDeserializer; +import software.amazon.smithy.java.core.serde.ShapeSerializer; +import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.HttpHeaderTrait; +import software.amazon.smithy.model.traits.RequiredTrait; +import software.amazon.smithy.utils.SmithyGenerated; + +@SmithyGenerated +public final class GetExperimentConfigOutput implements SerializableStruct { + public static final ShapeId $ID = ShapeId.from("io.superposition#GetExperimentConfigOutput"); + + public static final Schema $SCHEMA = Schema.structureBuilder($ID) + .putMember("last_modified", SharedSchemas.DATE_TIME, + new HttpHeaderTrait("last-modified"), + new RequiredTrait()) + .putMember("experiments", SharedSchemas.EXPERIMENT_LIST, + new RequiredTrait()) + .putMember("experiment_groups", SharedSchemas.EXPERIMENT_GROUP_LIST, + new RequiredTrait()) + .build(); + + private static final Schema $SCHEMA_LAST_MODIFIED = $SCHEMA.member("last_modified"); + private static final Schema $SCHEMA_EXPERIMENTS = $SCHEMA.member("experiments"); + private static final Schema $SCHEMA_EXPERIMENT_GROUPS = $SCHEMA.member("experiment_groups"); + + private final transient Instant lastModified; + private final transient List experiments; + private final transient List experimentGroups; + + private GetExperimentConfigOutput(Builder builder) { + this.lastModified = builder.lastModified; + this.experiments = Collections.unmodifiableList(builder.experiments); + this.experimentGroups = Collections.unmodifiableList(builder.experimentGroups); + } + + public Instant lastModified() { + return lastModified; + } + + public List experiments() { + return experiments; + } + + public boolean hasExperiments() { + return true; + } + + public List experimentGroups() { + return experimentGroups; + } + + public boolean hasExperimentGroups() { + return true; + } + + @Override + public String toString() { + return ToStringSerializer.serialize(this); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + GetExperimentConfigOutput that = (GetExperimentConfigOutput) other; + return Objects.equals(this.lastModified, that.lastModified) + && Objects.equals(this.experiments, that.experiments) + && Objects.equals(this.experimentGroups, that.experimentGroups); + } + + @Override + public int hashCode() { + return Objects.hash(lastModified, experiments, experimentGroups); + } + + @Override + public Schema schema() { + return $SCHEMA; + } + + @Override + public void serializeMembers(ShapeSerializer serializer) { + serializer.writeTimestamp($SCHEMA_LAST_MODIFIED, lastModified); + serializer.writeList($SCHEMA_EXPERIMENTS, experiments, experiments.size(), SharedSerde.ExperimentListSerializer.INSTANCE); + serializer.writeList($SCHEMA_EXPERIMENT_GROUPS, experimentGroups, experimentGroups.size(), SharedSerde.ExperimentGroupListSerializer.INSTANCE); + } + + @Override + @SuppressWarnings("unchecked") + public T getMemberValue(Schema member) { + return switch (member.memberIndex()) { + case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED, member, lastModified); + case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENTS, member, experiments); + case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_GROUPS, member, experimentGroups); + default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); + }; + } + + /** + * Create a new builder containing all the current property values of this object. + * + *

Note: This method performs only a shallow copy of the original properties. + * + * @return a builder for {@link GetExperimentConfigOutput}. + */ + public Builder toBuilder() { + var builder = new Builder(); + builder.lastModified(this.lastModified); + builder.experiments(this.experiments); + builder.experimentGroups(this.experimentGroups); + return builder; + } + + /** + * @return returns a new Builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link GetExperimentConfigOutput}. + */ + public static final class Builder implements ShapeBuilder { + private final PresenceTracker tracker = PresenceTracker.of($SCHEMA); + private Instant lastModified; + private List experiments; + private List experimentGroups; + + private Builder() {} + + @Override + public Schema schema() { + return $SCHEMA; + } + + /** + *

Required + * @return this builder. + */ + public Builder lastModified(Instant lastModified) { + this.lastModified = Objects.requireNonNull(lastModified, "lastModified cannot be null"); + tracker.setMember($SCHEMA_LAST_MODIFIED); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder experiments(List experiments) { + this.experiments = Objects.requireNonNull(experiments, "experiments cannot be null"); + tracker.setMember($SCHEMA_EXPERIMENTS); + return this; + } + + /** + *

Required + * @return this builder. + */ + public Builder experimentGroups(List experimentGroups) { + this.experimentGroups = Objects.requireNonNull(experimentGroups, "experimentGroups cannot be null"); + tracker.setMember($SCHEMA_EXPERIMENT_GROUPS); + return this; + } + + @Override + public GetExperimentConfigOutput build() { + tracker.validate(); + return new GetExperimentConfigOutput(this); + } + + @Override + @SuppressWarnings("unchecked") + public void setMemberValue(Schema member, Object value) { + switch (member.memberIndex()) { + case 0 -> lastModified((Instant) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED, member, value)); + case 1 -> experiments((List) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENTS, member, value)); + case 2 -> experimentGroups((List) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_GROUPS, member, value)); + default -> ShapeBuilder.super.setMemberValue(member, value); + } + } + + @Override + public ShapeBuilder errorCorrection() { + if (tracker.allSet()) { + return this; + } + if (!tracker.checkMember($SCHEMA_LAST_MODIFIED)) { + lastModified(Instant.EPOCH); + } + if (!tracker.checkMember($SCHEMA_EXPERIMENTS)) { + experiments(Collections.emptyList()); + } + if (!tracker.checkMember($SCHEMA_EXPERIMENT_GROUPS)) { + experimentGroups(Collections.emptyList()); + } + return this; + } + + @Override + public Builder deserialize(ShapeDeserializer decoder) { + decoder.readStruct($SCHEMA, this, $InnerDeserializer.INSTANCE); + return this; + } + + @Override + public Builder deserializeMember(ShapeDeserializer decoder, Schema schema) { + decoder.readStruct(schema.assertMemberTargetIs($SCHEMA), this, $InnerDeserializer.INSTANCE); + return this; + } + + private static final class $InnerDeserializer implements ShapeDeserializer.StructMemberConsumer { + private static final $InnerDeserializer INSTANCE = new $InnerDeserializer(); + + @Override + public void accept(Builder builder, Schema member, ShapeDeserializer de) { + switch (member.memberIndex()) { + case 0 -> builder.lastModified(de.readTimestamp(member)); + case 1 -> builder.experiments(SharedSerde.deserializeExperimentList(member, de)); + case 2 -> builder.experimentGroups(SharedSerde.deserializeExperimentGroupList(member, de)); + default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); + } + } + } + } +} + diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperiment.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperiment.java index fc9d98196..4e1f3950c 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperiment.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperiment.java @@ -23,7 +23,7 @@ public final class ListExperiment implements ApiOperation groupType; + private final transient DimensionMatchStrategy dimensionMatchStrategy; + private final transient Map context; private ListExperimentGroupsInput(Builder builder) { this.count = builder.count; @@ -80,12 +94,15 @@ private ListExperimentGroupsInput(Builder builder) { this.all = builder.all; this.workspaceId = builder.workspaceId; this.orgId = builder.orgId; + this.ifModifiedSince = builder.ifModifiedSince; this.name = builder.name; this.createdBy = builder.createdBy; this.lastModifiedBy = builder.lastModifiedBy; this.sortOn = builder.sortOn; this.sortBy = builder.sortBy; this.groupType = builder.groupType == null ? null : Collections.unmodifiableList(builder.groupType); + this.dimensionMatchStrategy = builder.dimensionMatchStrategy; + this.context = builder.context == null ? null : Collections.unmodifiableMap(builder.context); } /** @@ -117,6 +134,15 @@ public String orgId() { return orgId; } + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking + * the response code of the http response. This is required to make sure that clients can cache the + * response and avoid unnecessary calls when there are no updates. + */ + public Instant ifModifiedSince() { + return ifModifiedSince; + } + /** * Filter by experiment group name (exact match or substring, depending on backend implementation). */ @@ -166,6 +192,21 @@ public boolean hasGroupType() { return groupType != null; } + public DimensionMatchStrategy dimensionMatchStrategy() { + return dimensionMatchStrategy; + } + + public Map context() { + if (context == null) { + return Collections.emptyMap(); + } + return context; + } + + public boolean hasContext() { + return context != null; + } + @Override public String toString() { return ToStringSerializer.serialize(this); @@ -185,17 +226,20 @@ public boolean equals(Object other) { && Objects.equals(this.all, that.all) && Objects.equals(this.workspaceId, that.workspaceId) && Objects.equals(this.orgId, that.orgId) + && Objects.equals(this.ifModifiedSince, that.ifModifiedSince) && Objects.equals(this.name, that.name) && Objects.equals(this.createdBy, that.createdBy) && Objects.equals(this.lastModifiedBy, that.lastModifiedBy) && Objects.equals(this.sortOn, that.sortOn) && Objects.equals(this.sortBy, that.sortBy) - && Objects.equals(this.groupType, that.groupType); + && Objects.equals(this.groupType, that.groupType) + && Objects.equals(this.dimensionMatchStrategy, that.dimensionMatchStrategy) + && Objects.equals(this.context, that.context); } @Override public int hashCode() { - return Objects.hash(count, page, all, workspaceId, orgId, name, createdBy, lastModifiedBy, sortOn, sortBy, groupType); + return Objects.hash(count, page, all, workspaceId, orgId, ifModifiedSince, name, createdBy, lastModifiedBy, sortOn, sortBy, groupType, dimensionMatchStrategy, context); } @Override @@ -216,6 +260,9 @@ public void serializeMembers(ShapeSerializer serializer) { } serializer.writeString($SCHEMA_WORKSPACE_ID, workspaceId); serializer.writeString($SCHEMA_ORG_ID, orgId); + if (ifModifiedSince != null) { + serializer.writeTimestamp($SCHEMA_IF_MODIFIED_SINCE, ifModifiedSince); + } if (name != null) { serializer.writeString($SCHEMA_NAME, name); } @@ -234,6 +281,12 @@ public void serializeMembers(ShapeSerializer serializer) { if (groupType != null) { serializer.writeList($SCHEMA_GROUP_TYPE, groupType, groupType.size(), SharedSerde.GroupTypeListSerializer.INSTANCE); } + if (dimensionMatchStrategy != null) { + serializer.writeString($SCHEMA_DIMENSION_MATCH_STRATEGY, dimensionMatchStrategy.value()); + } + if (context != null) { + serializer.writeMap($SCHEMA_CONTEXT, context, context.size(), SharedSerde.ContextMapSerializer.INSTANCE); + } } @Override @@ -245,12 +298,15 @@ public T getMemberValue(Schema member) { case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_COUNT, member, count); case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_PAGE, member, page); case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_ALL, member, all); - case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_NAME, member, name); - case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_CREATED_BY, member, createdBy); - case 7 -> (T) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED_BY, member, lastModifiedBy); - case 8 -> (T) SchemaUtils.validateSameMember($SCHEMA_SORT_ON, member, sortOn); - case 9 -> (T) SchemaUtils.validateSameMember($SCHEMA_SORT_BY, member, sortBy); - case 10 -> (T) SchemaUtils.validateSameMember($SCHEMA_GROUP_TYPE, member, groupType); + case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, ifModifiedSince); + case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_NAME, member, name); + case 7 -> (T) SchemaUtils.validateSameMember($SCHEMA_CREATED_BY, member, createdBy); + case 8 -> (T) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED_BY, member, lastModifiedBy); + case 9 -> (T) SchemaUtils.validateSameMember($SCHEMA_SORT_ON, member, sortOn); + case 10 -> (T) SchemaUtils.validateSameMember($SCHEMA_SORT_BY, member, sortBy); + case 11 -> (T) SchemaUtils.validateSameMember($SCHEMA_GROUP_TYPE, member, groupType); + case 12 -> (T) SchemaUtils.validateSameMember($SCHEMA_DIMENSION_MATCH_STRATEGY, member, dimensionMatchStrategy); + case 13 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONTEXT, member, context); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -269,12 +325,15 @@ public Builder toBuilder() { builder.all(this.all); builder.workspaceId(this.workspaceId); builder.orgId(this.orgId); + builder.ifModifiedSince(this.ifModifiedSince); builder.name(this.name); builder.createdBy(this.createdBy); builder.lastModifiedBy(this.lastModifiedBy); builder.sortOn(this.sortOn); builder.sortBy(this.sortBy); builder.groupType(this.groupType); + builder.dimensionMatchStrategy(this.dimensionMatchStrategy); + builder.context(this.context); return builder; } @@ -295,12 +354,15 @@ public static final class Builder implements ShapeBuilder groupType; + private DimensionMatchStrategy dimensionMatchStrategy; + private Map context; private Builder() {} @@ -359,6 +421,18 @@ public Builder orgId(String orgId) { return this; } + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking + * the response code of the http response. This is required to make sure that clients can cache the + * response and avoid unnecessary calls when there are no updates. + * + * @return this builder. + */ + public Builder ifModifiedSince(Instant ifModifiedSince) { + this.ifModifiedSince = ifModifiedSince; + return this; + } + /** * Filter by experiment group name (exact match or substring, depending on backend implementation). * @@ -419,6 +493,22 @@ public Builder groupType(List groupType) { return this; } + /** + * @return this builder. + */ + public Builder dimensionMatchStrategy(DimensionMatchStrategy dimensionMatchStrategy) { + this.dimensionMatchStrategy = dimensionMatchStrategy; + return this; + } + + /** + * @return this builder. + */ + public Builder context(Map context) { + this.context = context; + return this; + } + @Override public ListExperimentGroupsInput build() { tracker.validate(); @@ -434,12 +524,15 @@ public void setMemberValue(Schema member, Object value) { case 2 -> count((int) SchemaUtils.validateSameMember($SCHEMA_COUNT, member, value)); case 3 -> page((int) SchemaUtils.validateSameMember($SCHEMA_PAGE, member, value)); case 4 -> all((boolean) SchemaUtils.validateSameMember($SCHEMA_ALL, member, value)); - case 5 -> name((String) SchemaUtils.validateSameMember($SCHEMA_NAME, member, value)); - case 6 -> createdBy((String) SchemaUtils.validateSameMember($SCHEMA_CREATED_BY, member, value)); - case 7 -> lastModifiedBy((String) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED_BY, member, value)); - case 8 -> sortOn((ExperimentGroupSortOn) SchemaUtils.validateSameMember($SCHEMA_SORT_ON, member, value)); - case 9 -> sortBy((SortBy) SchemaUtils.validateSameMember($SCHEMA_SORT_BY, member, value)); - case 10 -> groupType((List) SchemaUtils.validateSameMember($SCHEMA_GROUP_TYPE, member, value)); + case 5 -> ifModifiedSince((Instant) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, value)); + case 6 -> name((String) SchemaUtils.validateSameMember($SCHEMA_NAME, member, value)); + case 7 -> createdBy((String) SchemaUtils.validateSameMember($SCHEMA_CREATED_BY, member, value)); + case 8 -> lastModifiedBy((String) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED_BY, member, value)); + case 9 -> sortOn((ExperimentGroupSortOn) SchemaUtils.validateSameMember($SCHEMA_SORT_ON, member, value)); + case 10 -> sortBy((SortBy) SchemaUtils.validateSameMember($SCHEMA_SORT_BY, member, value)); + case 11 -> groupType((List) SchemaUtils.validateSameMember($SCHEMA_GROUP_TYPE, member, value)); + case 12 -> dimensionMatchStrategy((DimensionMatchStrategy) SchemaUtils.validateSameMember($SCHEMA_DIMENSION_MATCH_STRATEGY, member, value)); + case 13 -> context((Map) SchemaUtils.validateSameMember($SCHEMA_CONTEXT, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -481,12 +574,15 @@ public void accept(Builder builder, Schema member, ShapeDeserializer de) { case 2 -> builder.count(de.readInteger(member)); case 3 -> builder.page(de.readInteger(member)); case 4 -> builder.all(de.readBoolean(member)); - case 5 -> builder.name(de.readString(member)); - case 6 -> builder.createdBy(de.readString(member)); - case 7 -> builder.lastModifiedBy(de.readString(member)); - case 8 -> builder.sortOn(ExperimentGroupSortOn.builder().deserializeMember(de, member).build()); - case 9 -> builder.sortBy(SortBy.builder().deserializeMember(de, member).build()); - case 10 -> builder.groupType(SharedSerde.deserializeGroupTypeList(member, de)); + case 5 -> builder.ifModifiedSince(de.readTimestamp(member)); + case 6 -> builder.name(de.readString(member)); + case 7 -> builder.createdBy(de.readString(member)); + case 8 -> builder.lastModifiedBy(de.readString(member)); + case 9 -> builder.sortOn(ExperimentGroupSortOn.builder().deserializeMember(de, member).build()); + case 10 -> builder.sortBy(SortBy.builder().deserializeMember(de, member).build()); + case 11 -> builder.groupType(SharedSerde.deserializeGroupTypeList(member, de)); + case 12 -> builder.dimensionMatchStrategy(DimensionMatchStrategy.builder().deserializeMember(de, member).build()); + case 13 -> builder.context(SharedSerde.deserializeContextMap(member, de)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentGroupsOutput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentGroupsOutput.java index 159345af5..e9b45d6be 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentGroupsOutput.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentGroupsOutput.java @@ -1,6 +1,7 @@ package io.juspay.superposition.model; +import java.time.Instant; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -14,6 +15,7 @@ import software.amazon.smithy.java.core.serde.ShapeSerializer; import software.amazon.smithy.java.core.serde.ToStringSerializer; import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.HttpHeaderTrait; import software.amazon.smithy.model.traits.RequiredTrait; import software.amazon.smithy.utils.SmithyGenerated; @@ -28,20 +30,26 @@ public final class ListExperimentGroupsOutput implements SerializableStruct { new RequiredTrait()) .putMember("data", SharedSchemas.EXPERIMENT_GROUP_LIST, new RequiredTrait()) + .putMember("last_modified", SharedSchemas.DATE_TIME, + new HttpHeaderTrait("last-modified"), + new RequiredTrait()) .build(); private static final Schema $SCHEMA_TOTAL_PAGES = $SCHEMA.member("total_pages"); private static final Schema $SCHEMA_TOTAL_ITEMS = $SCHEMA.member("total_items"); private static final Schema $SCHEMA_DATA = $SCHEMA.member("data"); + private static final Schema $SCHEMA_LAST_MODIFIED = $SCHEMA.member("last_modified"); private final transient int totalPages; private final transient int totalItems; private final transient List data; + private final transient Instant lastModified; private ListExperimentGroupsOutput(Builder builder) { this.totalPages = builder.totalPages; this.totalItems = builder.totalItems; this.data = Collections.unmodifiableList(builder.data); + this.lastModified = builder.lastModified; } public int totalPages() { @@ -60,6 +68,10 @@ public boolean hasData() { return true; } + public Instant lastModified() { + return lastModified; + } + @Override public String toString() { return ToStringSerializer.serialize(this); @@ -76,12 +88,13 @@ public boolean equals(Object other) { ListExperimentGroupsOutput that = (ListExperimentGroupsOutput) other; return this.totalPages == that.totalPages && this.totalItems == that.totalItems - && Objects.equals(this.data, that.data); + && Objects.equals(this.data, that.data) + && Objects.equals(this.lastModified, that.lastModified); } @Override public int hashCode() { - return Objects.hash(totalPages, totalItems, data); + return Objects.hash(totalPages, totalItems, data, lastModified); } @Override @@ -94,6 +107,7 @@ public void serializeMembers(ShapeSerializer serializer) { serializer.writeInteger($SCHEMA_TOTAL_PAGES, totalPages); serializer.writeInteger($SCHEMA_TOTAL_ITEMS, totalItems); serializer.writeList($SCHEMA_DATA, data, data.size(), SharedSerde.ExperimentGroupListSerializer.INSTANCE); + serializer.writeTimestamp($SCHEMA_LAST_MODIFIED, lastModified); } @Override @@ -103,6 +117,7 @@ public T getMemberValue(Schema member) { case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_TOTAL_PAGES, member, totalPages); case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_TOTAL_ITEMS, member, totalItems); case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_DATA, member, data); + case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED, member, lastModified); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -119,6 +134,7 @@ public Builder toBuilder() { builder.totalPages(this.totalPages); builder.totalItems(this.totalItems); builder.data(this.data); + builder.lastModified(this.lastModified); return builder; } @@ -137,6 +153,7 @@ public static final class Builder implements ShapeBuilder data; + private Instant lastModified; private Builder() {} @@ -175,6 +192,16 @@ public Builder data(List data) { return this; } + /** + *

Required + * @return this builder. + */ + public Builder lastModified(Instant lastModified) { + this.lastModified = Objects.requireNonNull(lastModified, "lastModified cannot be null"); + tracker.setMember($SCHEMA_LAST_MODIFIED); + return this; + } + @Override public ListExperimentGroupsOutput build() { tracker.validate(); @@ -188,6 +215,7 @@ public void setMemberValue(Schema member, Object value) { case 0 -> totalPages((int) SchemaUtils.validateSameMember($SCHEMA_TOTAL_PAGES, member, value)); case 1 -> totalItems((int) SchemaUtils.validateSameMember($SCHEMA_TOTAL_ITEMS, member, value)); case 2 -> data((List) SchemaUtils.validateSameMember($SCHEMA_DATA, member, value)); + case 3 -> lastModified((Instant) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -206,6 +234,9 @@ public ShapeBuilder errorCorrection() { if (!tracker.checkMember($SCHEMA_DATA)) { data(Collections.emptyList()); } + if (!tracker.checkMember($SCHEMA_LAST_MODIFIED)) { + lastModified(Instant.EPOCH); + } return this; } @@ -230,6 +261,7 @@ public void accept(Builder builder, Schema member, ShapeDeserializer de) { case 0 -> builder.totalPages(de.readInteger(member)); case 1 -> builder.totalItems(de.readInteger(member)); case 2 -> builder.data(SharedSerde.deserializeExperimentGroupList(member, de)); + case 3 -> builder.lastModified(de.readTimestamp(member)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentInput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentInput.java index 02fcb4786..6ae8fca70 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentInput.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentInput.java @@ -4,6 +4,7 @@ import java.time.Instant; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import software.amazon.smithy.java.core.schema.PreludeSchemas; import software.amazon.smithy.java.core.schema.PresenceTracker; @@ -14,6 +15,7 @@ import software.amazon.smithy.java.core.serde.ShapeDeserializer; import software.amazon.smithy.java.core.serde.ShapeSerializer; import software.amazon.smithy.java.core.serde.ToStringSerializer; +import software.amazon.smithy.java.core.serde.document.Document; import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.traits.HttpHeaderTrait; import software.amazon.smithy.model.traits.HttpQueryTrait; @@ -37,6 +39,8 @@ public final class ListExperimentInput implements SerializableStruct { .putMember("org_id", PreludeSchemas.STRING, new HttpHeaderTrait("x-org-id"), new RequiredTrait()) + .putMember("if_modified_since", SharedSchemas.DATE_TIME, + new HttpHeaderTrait("if-modified-since")) .putMember("status", SharedSchemas.EXPERIMENT_STATUS_TYPE_LIST, new HttpQueryTrait("status")) .putMember("from_date", SharedSchemas.DATE_TIME, @@ -59,6 +63,9 @@ public final class ListExperimentInput implements SerializableStruct { new HttpQueryTrait("global_experiments_only")) .putMember("dimension_match_strategy", DimensionMatchStrategy.$SCHEMA, new HttpQueryTrait("dimension_match_strategy")) + .putMember("prefix", SharedSchemas.STRING_LIST, + new HttpQueryTrait("prefix")) + .putMember("context", SharedSchemas.CONTEXT_MAP) .build(); private static final Schema $SCHEMA_COUNT = $SCHEMA.member("count"); @@ -66,6 +73,7 @@ public final class ListExperimentInput implements SerializableStruct { private static final Schema $SCHEMA_ALL = $SCHEMA.member("all"); private static final Schema $SCHEMA_WORKSPACE_ID = $SCHEMA.member("workspace_id"); private static final Schema $SCHEMA_ORG_ID = $SCHEMA.member("org_id"); + private static final Schema $SCHEMA_IF_MODIFIED_SINCE = $SCHEMA.member("if_modified_since"); private static final Schema $SCHEMA_STATUS = $SCHEMA.member("status"); private static final Schema $SCHEMA_FROM_DATE = $SCHEMA.member("from_date"); private static final Schema $SCHEMA_TO_DATE = $SCHEMA.member("to_date"); @@ -77,12 +85,15 @@ public final class ListExperimentInput implements SerializableStruct { private static final Schema $SCHEMA_SORT_BY = $SCHEMA.member("sort_by"); private static final Schema $SCHEMA_GLOBAL_EXPERIMENTS_ONLY = $SCHEMA.member("global_experiments_only"); private static final Schema $SCHEMA_DIMENSION_MATCH_STRATEGY = $SCHEMA.member("dimension_match_strategy"); + private static final Schema $SCHEMA_PREFIX = $SCHEMA.member("prefix"); + private static final Schema $SCHEMA_CONTEXT = $SCHEMA.member("context"); private final transient Integer count; private final transient Integer page; private final transient Boolean all; private final transient String workspaceId; private final transient String orgId; + private final transient Instant ifModifiedSince; private final transient List status; private final transient Instant fromDate; private final transient Instant toDate; @@ -94,6 +105,8 @@ public final class ListExperimentInput implements SerializableStruct { private final transient SortBy sortBy; private final transient Boolean globalExperimentsOnly; private final transient DimensionMatchStrategy dimensionMatchStrategy; + private final transient List prefix; + private final transient Map context; private ListExperimentInput(Builder builder) { this.count = builder.count; @@ -101,6 +114,7 @@ private ListExperimentInput(Builder builder) { this.all = builder.all; this.workspaceId = builder.workspaceId; this.orgId = builder.orgId; + this.ifModifiedSince = builder.ifModifiedSince; this.status = builder.status == null ? null : Collections.unmodifiableList(builder.status); this.fromDate = builder.fromDate; this.toDate = builder.toDate; @@ -112,6 +126,8 @@ private ListExperimentInput(Builder builder) { this.sortBy = builder.sortBy; this.globalExperimentsOnly = builder.globalExperimentsOnly; this.dimensionMatchStrategy = builder.dimensionMatchStrategy; + this.prefix = builder.prefix == null ? null : Collections.unmodifiableList(builder.prefix); + this.context = builder.context == null ? null : Collections.unmodifiableMap(builder.context); } /** @@ -143,6 +159,15 @@ public String orgId() { return orgId; } + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking + * the response code of the http response. This is required to make sure that clients can cache the + * response and avoid unnecessary calls when there are no updates. + */ + public Instant ifModifiedSince() { + return ifModifiedSince; + } + public List status() { if (status == null) { return Collections.emptyList(); @@ -215,6 +240,28 @@ public DimensionMatchStrategy dimensionMatchStrategy() { return dimensionMatchStrategy; } + public List prefix() { + if (prefix == null) { + return Collections.emptyList(); + } + return prefix; + } + + public boolean hasPrefix() { + return prefix != null; + } + + public Map context() { + if (context == null) { + return Collections.emptyMap(); + } + return context; + } + + public boolean hasContext() { + return context != null; + } + @Override public String toString() { return ToStringSerializer.serialize(this); @@ -234,6 +281,7 @@ public boolean equals(Object other) { && Objects.equals(this.all, that.all) && Objects.equals(this.workspaceId, that.workspaceId) && Objects.equals(this.orgId, that.orgId) + && Objects.equals(this.ifModifiedSince, that.ifModifiedSince) && Objects.equals(this.status, that.status) && Objects.equals(this.fromDate, that.fromDate) && Objects.equals(this.toDate, that.toDate) @@ -244,12 +292,14 @@ public boolean equals(Object other) { && Objects.equals(this.sortOn, that.sortOn) && Objects.equals(this.sortBy, that.sortBy) && Objects.equals(this.globalExperimentsOnly, that.globalExperimentsOnly) - && Objects.equals(this.dimensionMatchStrategy, that.dimensionMatchStrategy); + && Objects.equals(this.dimensionMatchStrategy, that.dimensionMatchStrategy) + && Objects.equals(this.prefix, that.prefix) + && Objects.equals(this.context, that.context); } @Override public int hashCode() { - return Objects.hash(count, page, all, workspaceId, orgId, status, fromDate, toDate, experimentName, experimentIds, experimentGroupIds, createdBy, sortOn, sortBy, globalExperimentsOnly, dimensionMatchStrategy); + return Objects.hash(count, page, all, workspaceId, orgId, ifModifiedSince, status, fromDate, toDate, experimentName, experimentIds, experimentGroupIds, createdBy, sortOn, sortBy, globalExperimentsOnly, dimensionMatchStrategy, prefix, context); } @Override @@ -270,6 +320,9 @@ public void serializeMembers(ShapeSerializer serializer) { } serializer.writeString($SCHEMA_WORKSPACE_ID, workspaceId); serializer.writeString($SCHEMA_ORG_ID, orgId); + if (ifModifiedSince != null) { + serializer.writeTimestamp($SCHEMA_IF_MODIFIED_SINCE, ifModifiedSince); + } if (status != null) { serializer.writeList($SCHEMA_STATUS, status, status.size(), SharedSerde.ExperimentStatusTypeListSerializer.INSTANCE); } @@ -303,6 +356,12 @@ public void serializeMembers(ShapeSerializer serializer) { if (dimensionMatchStrategy != null) { serializer.writeString($SCHEMA_DIMENSION_MATCH_STRATEGY, dimensionMatchStrategy.value()); } + if (prefix != null) { + serializer.writeList($SCHEMA_PREFIX, prefix, prefix.size(), SharedSerde.StringListSerializer.INSTANCE); + } + if (context != null) { + serializer.writeMap($SCHEMA_CONTEXT, context, context.size(), SharedSerde.ContextMapSerializer.INSTANCE); + } } @Override @@ -314,17 +373,20 @@ public T getMemberValue(Schema member) { case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_COUNT, member, count); case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_PAGE, member, page); case 4 -> (T) SchemaUtils.validateSameMember($SCHEMA_ALL, member, all); - case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_STATUS, member, status); - case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_FROM_DATE, member, fromDate); - case 7 -> (T) SchemaUtils.validateSameMember($SCHEMA_TO_DATE, member, toDate); - case 8 -> (T) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_NAME, member, experimentName); - case 9 -> (T) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_IDS, member, experimentIds); - case 10 -> (T) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_GROUP_IDS, member, experimentGroupIds); - case 11 -> (T) SchemaUtils.validateSameMember($SCHEMA_CREATED_BY, member, createdBy); - case 12 -> (T) SchemaUtils.validateSameMember($SCHEMA_SORT_ON, member, sortOn); - case 13 -> (T) SchemaUtils.validateSameMember($SCHEMA_SORT_BY, member, sortBy); - case 14 -> (T) SchemaUtils.validateSameMember($SCHEMA_GLOBAL_EXPERIMENTS_ONLY, member, globalExperimentsOnly); - case 15 -> (T) SchemaUtils.validateSameMember($SCHEMA_DIMENSION_MATCH_STRATEGY, member, dimensionMatchStrategy); + case 5 -> (T) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, ifModifiedSince); + case 6 -> (T) SchemaUtils.validateSameMember($SCHEMA_STATUS, member, status); + case 7 -> (T) SchemaUtils.validateSameMember($SCHEMA_FROM_DATE, member, fromDate); + case 8 -> (T) SchemaUtils.validateSameMember($SCHEMA_TO_DATE, member, toDate); + case 9 -> (T) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_NAME, member, experimentName); + case 10 -> (T) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_IDS, member, experimentIds); + case 11 -> (T) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_GROUP_IDS, member, experimentGroupIds); + case 12 -> (T) SchemaUtils.validateSameMember($SCHEMA_CREATED_BY, member, createdBy); + case 13 -> (T) SchemaUtils.validateSameMember($SCHEMA_SORT_ON, member, sortOn); + case 14 -> (T) SchemaUtils.validateSameMember($SCHEMA_SORT_BY, member, sortBy); + case 15 -> (T) SchemaUtils.validateSameMember($SCHEMA_GLOBAL_EXPERIMENTS_ONLY, member, globalExperimentsOnly); + case 16 -> (T) SchemaUtils.validateSameMember($SCHEMA_DIMENSION_MATCH_STRATEGY, member, dimensionMatchStrategy); + case 17 -> (T) SchemaUtils.validateSameMember($SCHEMA_PREFIX, member, prefix); + case 18 -> (T) SchemaUtils.validateSameMember($SCHEMA_CONTEXT, member, context); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -343,6 +405,7 @@ public Builder toBuilder() { builder.all(this.all); builder.workspaceId(this.workspaceId); builder.orgId(this.orgId); + builder.ifModifiedSince(this.ifModifiedSince); builder.status(this.status); builder.fromDate(this.fromDate); builder.toDate(this.toDate); @@ -354,6 +417,8 @@ public Builder toBuilder() { builder.sortBy(this.sortBy); builder.globalExperimentsOnly(this.globalExperimentsOnly); builder.dimensionMatchStrategy(this.dimensionMatchStrategy); + builder.prefix(this.prefix); + builder.context(this.context); return builder; } @@ -374,6 +439,7 @@ public static final class Builder implements ShapeBuilder { private Boolean all; private String workspaceId; private String orgId; + private Instant ifModifiedSince; private List status; private Instant fromDate; private Instant toDate; @@ -385,6 +451,8 @@ public static final class Builder implements ShapeBuilder { private SortBy sortBy; private Boolean globalExperimentsOnly; private DimensionMatchStrategy dimensionMatchStrategy; + private List prefix; + private Map context; private Builder() {} @@ -443,6 +511,18 @@ public Builder orgId(String orgId) { return this; } + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking + * the response code of the http response. This is required to make sure that clients can cache the + * response and avoid unnecessary calls when there are no updates. + * + * @return this builder. + */ + public Builder ifModifiedSince(Instant ifModifiedSince) { + this.ifModifiedSince = ifModifiedSince; + return this; + } + /** * @return this builder. */ @@ -531,6 +611,22 @@ public Builder dimensionMatchStrategy(DimensionMatchStrategy dimensionMatchStrat return this; } + /** + * @return this builder. + */ + public Builder prefix(List prefix) { + this.prefix = prefix; + return this; + } + + /** + * @return this builder. + */ + public Builder context(Map context) { + this.context = context; + return this; + } + @Override public ListExperimentInput build() { tracker.validate(); @@ -546,17 +642,20 @@ public void setMemberValue(Schema member, Object value) { case 2 -> count((int) SchemaUtils.validateSameMember($SCHEMA_COUNT, member, value)); case 3 -> page((int) SchemaUtils.validateSameMember($SCHEMA_PAGE, member, value)); case 4 -> all((boolean) SchemaUtils.validateSameMember($SCHEMA_ALL, member, value)); - case 5 -> status((List) SchemaUtils.validateSameMember($SCHEMA_STATUS, member, value)); - case 6 -> fromDate((Instant) SchemaUtils.validateSameMember($SCHEMA_FROM_DATE, member, value)); - case 7 -> toDate((Instant) SchemaUtils.validateSameMember($SCHEMA_TO_DATE, member, value)); - case 8 -> experimentName((String) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_NAME, member, value)); - case 9 -> experimentIds((List) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_IDS, member, value)); - case 10 -> experimentGroupIds((List) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_GROUP_IDS, member, value)); - case 11 -> createdBy((List) SchemaUtils.validateSameMember($SCHEMA_CREATED_BY, member, value)); - case 12 -> sortOn((ExperimentSortOn) SchemaUtils.validateSameMember($SCHEMA_SORT_ON, member, value)); - case 13 -> sortBy((SortBy) SchemaUtils.validateSameMember($SCHEMA_SORT_BY, member, value)); - case 14 -> globalExperimentsOnly((boolean) SchemaUtils.validateSameMember($SCHEMA_GLOBAL_EXPERIMENTS_ONLY, member, value)); - case 15 -> dimensionMatchStrategy((DimensionMatchStrategy) SchemaUtils.validateSameMember($SCHEMA_DIMENSION_MATCH_STRATEGY, member, value)); + case 5 -> ifModifiedSince((Instant) SchemaUtils.validateSameMember($SCHEMA_IF_MODIFIED_SINCE, member, value)); + case 6 -> status((List) SchemaUtils.validateSameMember($SCHEMA_STATUS, member, value)); + case 7 -> fromDate((Instant) SchemaUtils.validateSameMember($SCHEMA_FROM_DATE, member, value)); + case 8 -> toDate((Instant) SchemaUtils.validateSameMember($SCHEMA_TO_DATE, member, value)); + case 9 -> experimentName((String) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_NAME, member, value)); + case 10 -> experimentIds((List) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_IDS, member, value)); + case 11 -> experimentGroupIds((List) SchemaUtils.validateSameMember($SCHEMA_EXPERIMENT_GROUP_IDS, member, value)); + case 12 -> createdBy((List) SchemaUtils.validateSameMember($SCHEMA_CREATED_BY, member, value)); + case 13 -> sortOn((ExperimentSortOn) SchemaUtils.validateSameMember($SCHEMA_SORT_ON, member, value)); + case 14 -> sortBy((SortBy) SchemaUtils.validateSameMember($SCHEMA_SORT_BY, member, value)); + case 15 -> globalExperimentsOnly((boolean) SchemaUtils.validateSameMember($SCHEMA_GLOBAL_EXPERIMENTS_ONLY, member, value)); + case 16 -> dimensionMatchStrategy((DimensionMatchStrategy) SchemaUtils.validateSameMember($SCHEMA_DIMENSION_MATCH_STRATEGY, member, value)); + case 17 -> prefix((List) SchemaUtils.validateSameMember($SCHEMA_PREFIX, member, value)); + case 18 -> context((Map) SchemaUtils.validateSameMember($SCHEMA_CONTEXT, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -598,17 +697,20 @@ public void accept(Builder builder, Schema member, ShapeDeserializer de) { case 2 -> builder.count(de.readInteger(member)); case 3 -> builder.page(de.readInteger(member)); case 4 -> builder.all(de.readBoolean(member)); - case 5 -> builder.status(SharedSerde.deserializeExperimentStatusTypeList(member, de)); - case 6 -> builder.fromDate(de.readTimestamp(member)); - case 7 -> builder.toDate(de.readTimestamp(member)); - case 8 -> builder.experimentName(de.readString(member)); - case 9 -> builder.experimentIds(SharedSerde.deserializeStringList(member, de)); - case 10 -> builder.experimentGroupIds(SharedSerde.deserializeStringList(member, de)); - case 11 -> builder.createdBy(SharedSerde.deserializeStringList(member, de)); - case 12 -> builder.sortOn(ExperimentSortOn.builder().deserializeMember(de, member).build()); - case 13 -> builder.sortBy(SortBy.builder().deserializeMember(de, member).build()); - case 14 -> builder.globalExperimentsOnly(de.readBoolean(member)); - case 15 -> builder.dimensionMatchStrategy(DimensionMatchStrategy.builder().deserializeMember(de, member).build()); + case 5 -> builder.ifModifiedSince(de.readTimestamp(member)); + case 6 -> builder.status(SharedSerde.deserializeExperimentStatusTypeList(member, de)); + case 7 -> builder.fromDate(de.readTimestamp(member)); + case 8 -> builder.toDate(de.readTimestamp(member)); + case 9 -> builder.experimentName(de.readString(member)); + case 10 -> builder.experimentIds(SharedSerde.deserializeStringList(member, de)); + case 11 -> builder.experimentGroupIds(SharedSerde.deserializeStringList(member, de)); + case 12 -> builder.createdBy(SharedSerde.deserializeStringList(member, de)); + case 13 -> builder.sortOn(ExperimentSortOn.builder().deserializeMember(de, member).build()); + case 14 -> builder.sortBy(SortBy.builder().deserializeMember(de, member).build()); + case 15 -> builder.globalExperimentsOnly(de.readBoolean(member)); + case 16 -> builder.dimensionMatchStrategy(DimensionMatchStrategy.builder().deserializeMember(de, member).build()); + case 17 -> builder.prefix(SharedSerde.deserializeStringList(member, de)); + case 18 -> builder.context(SharedSerde.deserializeContextMap(member, de)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentOutput.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentOutput.java index 8be7e008c..a63db2bb9 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentOutput.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/ListExperimentOutput.java @@ -1,6 +1,7 @@ package io.juspay.superposition.model; +import java.time.Instant; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -14,6 +15,7 @@ import software.amazon.smithy.java.core.serde.ShapeSerializer; import software.amazon.smithy.java.core.serde.ToStringSerializer; import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.HttpHeaderTrait; import software.amazon.smithy.model.traits.RequiredTrait; import software.amazon.smithy.utils.SmithyGenerated; @@ -28,20 +30,26 @@ public final class ListExperimentOutput implements SerializableStruct { new RequiredTrait()) .putMember("data", SharedSchemas.EXPERIMENT_LIST, new RequiredTrait()) + .putMember("last_modified", SharedSchemas.DATE_TIME, + new HttpHeaderTrait("last-modified"), + new RequiredTrait()) .build(); private static final Schema $SCHEMA_TOTAL_PAGES = $SCHEMA.member("total_pages"); private static final Schema $SCHEMA_TOTAL_ITEMS = $SCHEMA.member("total_items"); private static final Schema $SCHEMA_DATA = $SCHEMA.member("data"); + private static final Schema $SCHEMA_LAST_MODIFIED = $SCHEMA.member("last_modified"); private final transient int totalPages; private final transient int totalItems; private final transient List data; + private final transient Instant lastModified; private ListExperimentOutput(Builder builder) { this.totalPages = builder.totalPages; this.totalItems = builder.totalItems; this.data = Collections.unmodifiableList(builder.data); + this.lastModified = builder.lastModified; } public int totalPages() { @@ -60,6 +68,10 @@ public boolean hasData() { return true; } + public Instant lastModified() { + return lastModified; + } + @Override public String toString() { return ToStringSerializer.serialize(this); @@ -76,12 +88,13 @@ public boolean equals(Object other) { ListExperimentOutput that = (ListExperimentOutput) other; return this.totalPages == that.totalPages && this.totalItems == that.totalItems - && Objects.equals(this.data, that.data); + && Objects.equals(this.data, that.data) + && Objects.equals(this.lastModified, that.lastModified); } @Override public int hashCode() { - return Objects.hash(totalPages, totalItems, data); + return Objects.hash(totalPages, totalItems, data, lastModified); } @Override @@ -94,6 +107,7 @@ public void serializeMembers(ShapeSerializer serializer) { serializer.writeInteger($SCHEMA_TOTAL_PAGES, totalPages); serializer.writeInteger($SCHEMA_TOTAL_ITEMS, totalItems); serializer.writeList($SCHEMA_DATA, data, data.size(), SharedSerde.ExperimentListSerializer.INSTANCE); + serializer.writeTimestamp($SCHEMA_LAST_MODIFIED, lastModified); } @Override @@ -103,6 +117,7 @@ public T getMemberValue(Schema member) { case 0 -> (T) SchemaUtils.validateSameMember($SCHEMA_TOTAL_PAGES, member, totalPages); case 1 -> (T) SchemaUtils.validateSameMember($SCHEMA_TOTAL_ITEMS, member, totalItems); case 2 -> (T) SchemaUtils.validateSameMember($SCHEMA_DATA, member, data); + case 3 -> (T) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED, member, lastModified); default -> throw new IllegalArgumentException("Attempted to get non-existent member: " + member.id()); }; } @@ -119,6 +134,7 @@ public Builder toBuilder() { builder.totalPages(this.totalPages); builder.totalItems(this.totalItems); builder.data(this.data); + builder.lastModified(this.lastModified); return builder; } @@ -137,6 +153,7 @@ public static final class Builder implements ShapeBuilder private int totalPages; private int totalItems; private List data; + private Instant lastModified; private Builder() {} @@ -175,6 +192,16 @@ public Builder data(List data) { return this; } + /** + *

Required + * @return this builder. + */ + public Builder lastModified(Instant lastModified) { + this.lastModified = Objects.requireNonNull(lastModified, "lastModified cannot be null"); + tracker.setMember($SCHEMA_LAST_MODIFIED); + return this; + } + @Override public ListExperimentOutput build() { tracker.validate(); @@ -188,6 +215,7 @@ public void setMemberValue(Schema member, Object value) { case 0 -> totalPages((int) SchemaUtils.validateSameMember($SCHEMA_TOTAL_PAGES, member, value)); case 1 -> totalItems((int) SchemaUtils.validateSameMember($SCHEMA_TOTAL_ITEMS, member, value)); case 2 -> data((List) SchemaUtils.validateSameMember($SCHEMA_DATA, member, value)); + case 3 -> lastModified((Instant) SchemaUtils.validateSameMember($SCHEMA_LAST_MODIFIED, member, value)); default -> ShapeBuilder.super.setMemberValue(member, value); } } @@ -206,6 +234,9 @@ public ShapeBuilder errorCorrection() { if (!tracker.checkMember($SCHEMA_DATA)) { data(Collections.emptyList()); } + if (!tracker.checkMember($SCHEMA_LAST_MODIFIED)) { + lastModified(Instant.EPOCH); + } return this; } @@ -230,6 +261,7 @@ public void accept(Builder builder, Schema member, ShapeDeserializer de) { case 0 -> builder.totalPages(de.readInteger(member)); case 1 -> builder.totalItems(de.readInteger(member)); case 2 -> builder.data(SharedSerde.deserializeExperimentList(member, de)); + case 3 -> builder.lastModified(de.readTimestamp(member)); default -> throw new IllegalArgumentException("Unexpected member: " + member.memberName()); } } diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSchemas.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSchemas.java index 0dda12933..9b20500d6 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSchemas.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSchemas.java @@ -123,10 +123,6 @@ final class SharedSchemas { .putMember("member", DimensionResponse.$SCHEMA) .build(); - static final Schema GROUP_TYPE_LIST = Schema.listBuilder(ShapeId.from("io.superposition#GroupTypeList")) - .putMember("member", GroupType.$SCHEMA) - .build(); - static final Schema EXPERIMENT_GROUP_LIST = Schema.listBuilder(ShapeId.from("io.superposition#ExperimentGroupList")) .putMember("member", ExperimentGroupResponse.$SCHEMA) .build(); @@ -135,6 +131,10 @@ final class SharedSchemas { .putMember("member", ExperimentResponse.$SCHEMA) .build(); + static final Schema GROUP_TYPE_LIST = Schema.listBuilder(ShapeId.from("io.superposition#GroupTypeList")) + .putMember("member", GroupType.$SCHEMA) + .build(); + static final Schema EXPERIMENT_STATUS_TYPE_LIST = Schema.listBuilder(ShapeId.from("io.superposition#ExperimentStatusTypeList")) .putMember("member", ExperimentStatusType.$SCHEMA) .build(); diff --git a/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSerde.java b/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSerde.java index b7c3f081c..1f01f0db6 100644 --- a/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSerde.java +++ b/clients/java/sdk/src/main/java/io/juspay/superposition/model/SharedSerde.java @@ -328,6 +328,37 @@ public void accept(List state, ShapeDeserializer deseriali } } + static final class GroupTypeListSerializer implements BiConsumer, ShapeSerializer> { + static final GroupTypeListSerializer INSTANCE = new GroupTypeListSerializer(); + + @Override + public void accept(List values, ShapeSerializer serializer) { + for (var value : values) { + serializer.writeString(SharedSchemas.GROUP_TYPE_LIST.listMember(), value.value()); + } + } + } + + static List deserializeGroupTypeList(Schema schema, ShapeDeserializer deserializer) { + var size = deserializer.containerSize(); + List result = size == -1 ? new ArrayList<>() : new ArrayList<>(size); + deserializer.readList(schema, result, GroupTypeList$MemberDeserializer.INSTANCE); + return result; + } + + private static final class GroupTypeList$MemberDeserializer implements ShapeDeserializer.ListMemberConsumer> { + static final GroupTypeList$MemberDeserializer INSTANCE = new GroupTypeList$MemberDeserializer(); + + @Override + public void accept(List state, ShapeDeserializer deserializer) { + if (deserializer.isNull()) { + + return; + } + state.add(GroupType.builder().deserializeMember(deserializer, SharedSchemas.GROUP_TYPE_LIST.listMember()).build()); + } + } + static final class ExperimentListSerializer implements BiConsumer, ShapeSerializer> { static final ExperimentListSerializer INSTANCE = new ExperimentListSerializer(); @@ -390,37 +421,6 @@ public void accept(List state, ShapeDeserializer deseri } } - static final class GroupTypeListSerializer implements BiConsumer, ShapeSerializer> { - static final GroupTypeListSerializer INSTANCE = new GroupTypeListSerializer(); - - @Override - public void accept(List values, ShapeSerializer serializer) { - for (var value : values) { - serializer.writeString(SharedSchemas.GROUP_TYPE_LIST.listMember(), value.value()); - } - } - } - - static List deserializeGroupTypeList(Schema schema, ShapeDeserializer deserializer) { - var size = deserializer.containerSize(); - List result = size == -1 ? new ArrayList<>() : new ArrayList<>(size); - deserializer.readList(schema, result, GroupTypeList$MemberDeserializer.INSTANCE); - return result; - } - - private static final class GroupTypeList$MemberDeserializer implements ShapeDeserializer.ListMemberConsumer> { - static final GroupTypeList$MemberDeserializer INSTANCE = new GroupTypeList$MemberDeserializer(); - - @Override - public void accept(List state, ShapeDeserializer deserializer) { - if (deserializer.isNull()) { - - return; - } - state.add(GroupType.builder().deserializeMember(deserializer, SharedSchemas.GROUP_TYPE_LIST.listMember()).build()); - } - } - static final class DimensionListSerializer implements BiConsumer, ShapeSerializer> { static final DimensionListSerializer INSTANCE = new DimensionListSerializer(); diff --git a/clients/javascript/sdk/src/Superposition.ts b/clients/javascript/sdk/src/Superposition.ts index 6d816ec5d..ff43da09e 100644 --- a/clients/javascript/sdk/src/Superposition.ts +++ b/clients/javascript/sdk/src/Superposition.ts @@ -138,11 +138,6 @@ import { GetConfigCommandInput, GetConfigCommandOutput, } from "./commands/GetConfigCommand"; -import { - GetConfigFastCommand, - GetConfigFastCommandInput, - GetConfigFastCommandOutput, -} from "./commands/GetConfigFastCommand"; import { GetConfigJsonCommand, GetConfigJsonCommandInput, @@ -178,6 +173,11 @@ import { GetExperimentCommandInput, GetExperimentCommandOutput, } from "./commands/GetExperimentCommand"; +import { + GetExperimentConfigCommand, + GetExperimentConfigCommandInput, + GetExperimentConfigCommandOutput, +} from "./commands/GetExperimentConfigCommand"; import { GetExperimentGroupCommand, GetExperimentGroupCommandInput, @@ -459,7 +459,6 @@ const commands = { DeleteWebhookCommand, DiscardExperimentCommand, GetConfigCommand, - GetConfigFastCommand, GetConfigJsonCommand, GetConfigTomlCommand, GetContextCommand, @@ -467,6 +466,7 @@ const commands = { GetDefaultConfigCommand, GetDimensionCommand, GetExperimentCommand, + GetExperimentConfigCommand, GetExperimentGroupCommand, GetFunctionCommand, GetOrganisationCommand, @@ -979,23 +979,6 @@ export interface Superposition { cb: (err: any, data?: GetConfigCommandOutput) => void ): void; - /** - * @see {@link GetConfigFastCommand} - */ - getConfigFast( - args: GetConfigFastCommandInput, - options?: __HttpHandlerOptions, - ): Promise; - getConfigFast( - args: GetConfigFastCommandInput, - cb: (err: any, data?: GetConfigFastCommandOutput) => void - ): void; - getConfigFast( - args: GetConfigFastCommandInput, - options: __HttpHandlerOptions, - cb: (err: any, data?: GetConfigFastCommandOutput) => void - ): void; - /** * @see {@link GetConfigJsonCommand} */ @@ -1115,6 +1098,23 @@ export interface Superposition { cb: (err: any, data?: GetExperimentCommandOutput) => void ): void; + /** + * @see {@link GetExperimentConfigCommand} + */ + getExperimentConfig( + args: GetExperimentConfigCommandInput, + options?: __HttpHandlerOptions, + ): Promise; + getExperimentConfig( + args: GetExperimentConfigCommandInput, + cb: (err: any, data?: GetExperimentConfigCommandOutput) => void + ): void; + getExperimentConfig( + args: GetExperimentConfigCommandInput, + options: __HttpHandlerOptions, + cb: (err: any, data?: GetExperimentConfigCommandOutput) => void + ): void; + /** * @see {@link GetExperimentGroupCommand} */ diff --git a/clients/javascript/sdk/src/SuperpositionClient.ts b/clients/javascript/sdk/src/SuperpositionClient.ts index b64f6b602..79c78d7cb 100644 --- a/clients/javascript/sdk/src/SuperpositionClient.ts +++ b/clients/javascript/sdk/src/SuperpositionClient.ts @@ -113,10 +113,6 @@ import { GetConfigCommandInput, GetConfigCommandOutput, } from "./commands/GetConfigCommand"; -import { - GetConfigFastCommandInput, - GetConfigFastCommandOutput, -} from "./commands/GetConfigFastCommand"; import { GetConfigJsonCommandInput, GetConfigJsonCommandOutput, @@ -145,6 +141,10 @@ import { GetExperimentCommandInput, GetExperimentCommandOutput, } from "./commands/GetExperimentCommand"; +import { + GetExperimentConfigCommandInput, + GetExperimentConfigCommandOutput, +} from "./commands/GetExperimentConfigCommand"; import { GetExperimentGroupCommandInput, GetExperimentGroupCommandOutput, @@ -438,7 +438,6 @@ export type ServiceInputTypes = | DeleteWebhookCommandInput | DiscardExperimentCommandInput | GetConfigCommandInput - | GetConfigFastCommandInput | GetConfigJsonCommandInput | GetConfigTomlCommandInput | GetContextCommandInput @@ -446,6 +445,7 @@ export type ServiceInputTypes = | GetDefaultConfigCommandInput | GetDimensionCommandInput | GetExperimentCommandInput + | GetExperimentConfigCommandInput | GetExperimentGroupCommandInput | GetFunctionCommandInput | GetOrganisationCommandInput @@ -528,7 +528,6 @@ export type ServiceOutputTypes = | DeleteWebhookCommandOutput | DiscardExperimentCommandOutput | GetConfigCommandOutput - | GetConfigFastCommandOutput | GetConfigJsonCommandOutput | GetConfigTomlCommandOutput | GetContextCommandOutput @@ -536,6 +535,7 @@ export type ServiceOutputTypes = | GetDefaultConfigCommandOutput | GetDimensionCommandOutput | GetExperimentCommandOutput + | GetExperimentConfigCommandOutput | GetExperimentGroupCommandOutput | GetFunctionCommandOutput | GetOrganisationCommandOutput diff --git a/clients/javascript/sdk/src/commands/GetConfigCommand.ts b/clients/javascript/sdk/src/commands/GetConfigCommand.ts index 5ba65aee2..6c61d6e0b 100644 --- a/clients/javascript/sdk/src/commands/GetConfigCommand.ts +++ b/clients/javascript/sdk/src/commands/GetConfigCommand.ts @@ -49,6 +49,7 @@ export interface GetConfigCommandOutput extends GetConfigOutput, __MetadataBeare * "STRING_VALUE", * ], * version: "STRING_VALUE", + * if_modified_since: new Date("TIMESTAMP"), * context: { // ContextMap * "": "DOCUMENT_VALUE", * }, @@ -98,7 +99,6 @@ export interface GetConfigCommandOutput extends GetConfigOutput, __MetadataBeare * // }, * // version: "STRING_VALUE", // required * // last_modified: new Date("TIMESTAMP"), // required - * // audit_id: "STRING_VALUE", * // }; * * ``` diff --git a/clients/javascript/sdk/src/commands/GetConfigFastCommand.ts b/clients/javascript/sdk/src/commands/GetConfigFastCommand.ts deleted file mode 100644 index 003aa7154..000000000 --- a/clients/javascript/sdk/src/commands/GetConfigFastCommand.ts +++ /dev/null @@ -1,99 +0,0 @@ -// smithy-typescript generated code -import { - ServiceInputTypes, - ServiceOutputTypes, - SuperpositionClientResolvedConfig, -} from "../SuperpositionClient"; -import { - GetConfigFastInput, - GetConfigFastOutput, -} from "../models/models_0"; -import { - de_GetConfigFastCommand, - se_GetConfigFastCommand, -} from "../protocols/Aws_restJson1"; -import { getSerdePlugin } from "@smithy/middleware-serde"; -import { Command as $Command } from "@smithy/smithy-client"; -import { MetadataBearer as __MetadataBearer } from "@smithy/types"; - -/** - * @public - */ -export type { __MetadataBearer }; -export { $Command }; -/** - * @public - * - * The input for {@link GetConfigFastCommand}. - */ -export interface GetConfigFastCommandInput extends GetConfigFastInput {} -/** - * @public - * - * The output of {@link GetConfigFastCommand}. - */ -export interface GetConfigFastCommandOutput extends GetConfigFastOutput, __MetadataBearer {} - -/** - * Retrieves the latest config with no processing for high-performance access. - * @example - * Use a bare-bones client and the command you need to make an API call. - * ```javascript - * import { SuperpositionClient, GetConfigFastCommand } from "superposition-sdk"; // ES Modules import - * // const { SuperpositionClient, GetConfigFastCommand } = require("superposition-sdk"); // CommonJS import - * const client = new SuperpositionClient(config); - * const input = { // GetConfigFastInput - * workspace_id: "STRING_VALUE", // required - * org_id: "STRING_VALUE", // required - * }; - * const command = new GetConfigFastCommand(input); - * const response = await client.send(command); - * // { // GetConfigFastOutput - * // config: "DOCUMENT_VALUE", - * // version: "STRING_VALUE", - * // last_modified: new Date("TIMESTAMP"), - * // audit_id: "STRING_VALUE", - * // }; - * - * ``` - * - * @param GetConfigFastCommandInput - {@link GetConfigFastCommandInput} - * @returns {@link GetConfigFastCommandOutput} - * @see {@link GetConfigFastCommandInput} for command's `input` shape. - * @see {@link GetConfigFastCommandOutput} for command's `response` shape. - * @see {@link SuperpositionClientResolvedConfig | config} for SuperpositionClient's `config` shape. - * - * @throws {@link InternalServerError} (server fault) - * - * @throws {@link SuperpositionServiceException} - *

Base exception class for all service exceptions from Superposition service.

- * - * @public - */ -export class GetConfigFastCommand extends $Command.classBuilder() - .m(function (this: any, Command: any, cs: any, config: SuperpositionClientResolvedConfig, o: any) { - return [ - - getSerdePlugin(config, this.serialize, this.deserialize), - ]; - }) - .s("Superposition", "GetConfigFast", { - - }) - .n("SuperpositionClient", "GetConfigFastCommand") - .f(void 0, void 0) - .ser(se_GetConfigFastCommand) - .de(de_GetConfigFastCommand) -.build() { -/** @internal type navigation helper, not in runtime. */ -declare protected static __types: { - api: { - input: GetConfigFastInput; - output: GetConfigFastOutput; - }; - sdk: { - input: GetConfigFastCommandInput; - output: GetConfigFastCommandOutput; - }; -}; -} diff --git a/clients/javascript/sdk/src/commands/GetConfigJsonCommand.ts b/clients/javascript/sdk/src/commands/GetConfigJsonCommand.ts index 510be239b..3c28baa2e 100644 --- a/clients/javascript/sdk/src/commands/GetConfigJsonCommand.ts +++ b/clients/javascript/sdk/src/commands/GetConfigJsonCommand.ts @@ -45,6 +45,7 @@ export interface GetConfigJsonCommandOutput extends GetConfigJsonOutput, __Metad * const input = { // GetConfigJsonInput * workspace_id: "STRING_VALUE", // required * org_id: "STRING_VALUE", // required + * if_modified_since: new Date("TIMESTAMP"), * }; * const command = new GetConfigJsonCommand(input); * const response = await client.send(command); diff --git a/clients/javascript/sdk/src/commands/GetConfigTomlCommand.ts b/clients/javascript/sdk/src/commands/GetConfigTomlCommand.ts index fb2c33679..67d065eae 100644 --- a/clients/javascript/sdk/src/commands/GetConfigTomlCommand.ts +++ b/clients/javascript/sdk/src/commands/GetConfigTomlCommand.ts @@ -45,6 +45,7 @@ export interface GetConfigTomlCommandOutput extends GetConfigTomlOutput, __Metad * const input = { // GetConfigTomlInput * workspace_id: "STRING_VALUE", // required * org_id: "STRING_VALUE", // required + * if_modified_since: new Date("TIMESTAMP"), * }; * const command = new GetConfigTomlCommand(input); * const response = await client.send(command); diff --git a/clients/javascript/sdk/src/commands/GetExperimentConfigCommand.ts b/clients/javascript/sdk/src/commands/GetExperimentConfigCommand.ts new file mode 100644 index 000000000..89805641c --- /dev/null +++ b/clients/javascript/sdk/src/commands/GetExperimentConfigCommand.ts @@ -0,0 +1,169 @@ +// smithy-typescript generated code +import { + ServiceInputTypes, + ServiceOutputTypes, + SuperpositionClientResolvedConfig, +} from "../SuperpositionClient"; +import { + GetExperimentConfigInput, + GetExperimentConfigOutput, +} from "../models/models_0"; +import { + de_GetExperimentConfigCommand, + se_GetExperimentConfigCommand, +} from "../protocols/Aws_restJson1"; +import { getSerdePlugin } from "@smithy/middleware-serde"; +import { Command as $Command } from "@smithy/smithy-client"; +import { MetadataBearer as __MetadataBearer } from "@smithy/types"; + +/** + * @public + */ +export type { __MetadataBearer }; +export { $Command }; +/** + * @public + * + * The input for {@link GetExperimentConfigCommand}. + */ +export interface GetExperimentConfigCommandInput extends GetExperimentConfigInput {} +/** + * @public + * + * The output of {@link GetExperimentConfigCommand}. + */ +export interface GetExperimentConfigCommandOutput extends GetExperimentConfigOutput, __MetadataBearer {} + +/** + * Retrieves the experiment configuration for a given workspace and organization. The response includes details of all experiment groups and experiments that match the specified filters. + * @example + * Use a bare-bones client and the command you need to make an API call. + * ```javascript + * import { SuperpositionClient, GetExperimentConfigCommand } from "superposition-sdk"; // ES Modules import + * // const { SuperpositionClient, GetExperimentConfigCommand } = require("superposition-sdk"); // CommonJS import + * const client = new SuperpositionClient(config); + * const input = { // GetExperimentConfigInput + * workspace_id: "STRING_VALUE", // required + * org_id: "STRING_VALUE", // required + * if_modified_since: new Date("TIMESTAMP"), + * prefix: [ // StringList + * "STRING_VALUE", + * ], + * context: { // ContextMap + * "": "DOCUMENT_VALUE", + * }, + * dimension_match_strategy: "exact" || "subset", + * }; + * const command = new GetExperimentConfigCommand(input); + * const response = await client.send(command); + * // { // GetExperimentConfigOutput + * // last_modified: new Date("TIMESTAMP"), // required + * // experiments: [ // ExperimentList // required + * // { // ExperimentResponse + * // id: "STRING_VALUE", // required + * // created_at: new Date("TIMESTAMP"), // required + * // created_by: "STRING_VALUE", // required + * // last_modified: new Date("TIMESTAMP"), // required + * // name: "STRING_VALUE", // required + * // experiment_type: "DEFAULT" || "DELETE_OVERRIDES", // required + * // override_keys: [ // ListOverrideKeys // required + * // "STRING_VALUE", + * // ], + * // status: "CREATED" || "CONCLUDED" || "INPROGRESS" || "DISCARDED" || "PAUSED", // required + * // traffic_percentage: Number("int"), // required + * // context: { // Condition // required + * // "": "DOCUMENT_VALUE", + * // }, + * // variants: [ // ListVariant // required + * // { // Variant + * // id: "STRING_VALUE", // required + * // variant_type: "CONTROL" || "EXPERIMENTAL", // required + * // context_id: "STRING_VALUE", + * // override_id: "STRING_VALUE", + * // overrides: { // Overrides // required + * // "": "DOCUMENT_VALUE", + * // }, + * // }, + * // ], + * // last_modified_by: "STRING_VALUE", // required + * // chosen_variant: "STRING_VALUE", + * // description: "STRING_VALUE", // required + * // change_reason: "STRING_VALUE", // required + * // started_at: new Date("TIMESTAMP"), + * // started_by: "STRING_VALUE", + * // metrics_url: "STRING_VALUE", + * // metrics: "DOCUMENT_VALUE", + * // experiment_group_id: "STRING_VALUE", + * // }, + * // ], + * // experiment_groups: [ // ExperimentGroupList // required + * // { // ExperimentGroupResponse + * // id: "STRING_VALUE", // required + * // context_hash: "STRING_VALUE", // required + * // name: "STRING_VALUE", // required + * // description: "STRING_VALUE", // required + * // change_reason: "STRING_VALUE", // required + * // context: { // required + * // "": "DOCUMENT_VALUE", + * // }, + * // traffic_percentage: Number("int"), // required + * // member_experiment_ids: [ // StringList // required + * // "STRING_VALUE", + * // ], + * // created_at: new Date("TIMESTAMP"), // required + * // created_by: "STRING_VALUE", // required + * // last_modified_at: new Date("TIMESTAMP"), // required + * // last_modified_by: "STRING_VALUE", // required + * // buckets: [ // Buckets // required + * // { // Bucket + * // experiment_id: "STRING_VALUE", // required + * // variant_id: "STRING_VALUE", // required + * // }, + * // ], + * // group_type: "USER_CREATED" || "SYSTEM_GENERATED", // required + * // }, + * // ], + * // }; + * + * ``` + * + * @param GetExperimentConfigCommandInput - {@link GetExperimentConfigCommandInput} + * @returns {@link GetExperimentConfigCommandOutput} + * @see {@link GetExperimentConfigCommandInput} for command's `input` shape. + * @see {@link GetExperimentConfigCommandOutput} for command's `response` shape. + * @see {@link SuperpositionClientResolvedConfig | config} for SuperpositionClient's `config` shape. + * + * @throws {@link InternalServerError} (server fault) + * + * @throws {@link SuperpositionServiceException} + *

Base exception class for all service exceptions from Superposition service.

+ * + * @public + */ +export class GetExperimentConfigCommand extends $Command.classBuilder() + .m(function (this: any, Command: any, cs: any, config: SuperpositionClientResolvedConfig, o: any) { + return [ + + getSerdePlugin(config, this.serialize, this.deserialize), + ]; + }) + .s("Superposition", "GetExperimentConfig", { + + }) + .n("SuperpositionClient", "GetExperimentConfigCommand") + .f(void 0, void 0) + .ser(se_GetExperimentConfigCommand) + .de(de_GetExperimentConfigCommand) +.build() { +/** @internal type navigation helper, not in runtime. */ +declare protected static __types: { + api: { + input: GetExperimentConfigInput; + output: GetExperimentConfigOutput; + }; + sdk: { + input: GetExperimentConfigCommandInput; + output: GetExperimentConfigCommandOutput; + }; +}; +} diff --git a/clients/javascript/sdk/src/commands/ListExperimentCommand.ts b/clients/javascript/sdk/src/commands/ListExperimentCommand.ts index 8bea91e1d..1c89d2d4c 100644 --- a/clients/javascript/sdk/src/commands/ListExperimentCommand.ts +++ b/clients/javascript/sdk/src/commands/ListExperimentCommand.ts @@ -48,6 +48,7 @@ export interface ListExperimentCommandOutput extends ListExperimentOutput, __Met * all: true || false, * workspace_id: "STRING_VALUE", // required * org_id: "STRING_VALUE", // required + * if_modified_since: new Date("TIMESTAMP"), * status: [ // ExperimentStatusTypeList * "CREATED" || "CONCLUDED" || "INPROGRESS" || "DISCARDED" || "PAUSED", * ], @@ -67,6 +68,12 @@ export interface ListExperimentCommandOutput extends ListExperimentOutput, __Met * sort_by: "desc" || "asc", * global_experiments_only: true || false, * dimension_match_strategy: "exact" || "subset", + * prefix: [ + * "STRING_VALUE", + * ], + * context: { // ContextMap + * "": "DOCUMENT_VALUE", + * }, * }; * const command = new ListExperimentCommand(input); * const response = await client.send(command); @@ -111,6 +118,7 @@ export interface ListExperimentCommandOutput extends ListExperimentOutput, __Met * // experiment_group_id: "STRING_VALUE", * // }, * // ], + * // last_modified: new Date("TIMESTAMP"), // required * // }; * * ``` diff --git a/clients/javascript/sdk/src/commands/ListExperimentGroupsCommand.ts b/clients/javascript/sdk/src/commands/ListExperimentGroupsCommand.ts index 7be2ee980..38d6a18f0 100644 --- a/clients/javascript/sdk/src/commands/ListExperimentGroupsCommand.ts +++ b/clients/javascript/sdk/src/commands/ListExperimentGroupsCommand.ts @@ -48,6 +48,7 @@ export interface ListExperimentGroupsCommandOutput extends ListExperimentGroupsO * all: true || false, * workspace_id: "STRING_VALUE", // required * org_id: "STRING_VALUE", // required + * if_modified_since: new Date("TIMESTAMP"), * name: "STRING_VALUE", * created_by: "STRING_VALUE", * last_modified_by: "STRING_VALUE", @@ -56,6 +57,10 @@ export interface ListExperimentGroupsCommandOutput extends ListExperimentGroupsO * group_type: [ // GroupTypeList * "USER_CREATED" || "SYSTEM_GENERATED", * ], + * dimension_match_strategy: "exact" || "subset", + * context: { // ContextMap + * "": "DOCUMENT_VALUE", + * }, * }; * const command = new ListExperimentGroupsCommand(input); * const response = await client.send(command); @@ -89,6 +94,7 @@ export interface ListExperimentGroupsCommandOutput extends ListExperimentGroupsO * // group_type: "USER_CREATED" || "SYSTEM_GENERATED", // required * // }, * // ], + * // last_modified: new Date("TIMESTAMP"), // required * // }; * * ``` diff --git a/clients/javascript/sdk/src/commands/index.ts b/clients/javascript/sdk/src/commands/index.ts index cdd7b238c..56e20dca1 100644 --- a/clients/javascript/sdk/src/commands/index.ts +++ b/clients/javascript/sdk/src/commands/index.ts @@ -26,7 +26,6 @@ export * from "./DeleteVariableCommand"; export * from "./DeleteWebhookCommand"; export * from "./DiscardExperimentCommand"; export * from "./GetConfigCommand"; -export * from "./GetConfigFastCommand"; export * from "./GetConfigJsonCommand"; export * from "./GetConfigTomlCommand"; export * from "./GetContextCommand"; @@ -34,6 +33,7 @@ export * from "./GetContextFromConditionCommand"; export * from "./GetDefaultConfigCommand"; export * from "./GetDimensionCommand"; export * from "./GetExperimentCommand"; +export * from "./GetExperimentConfigCommand"; export * from "./GetExperimentGroupCommand"; export * from "./GetFunctionCommand"; export * from "./GetOrganisationCommand"; diff --git a/clients/javascript/sdk/src/models/models_0.ts b/clients/javascript/sdk/src/models/models_0.ts index 81375bae9..22ee0b59c 100644 --- a/clients/javascript/sdk/src/models/models_0.ts +++ b/clients/javascript/sdk/src/models/models_0.ts @@ -657,6 +657,12 @@ export interface GetConfigInput { org_id: string | undefined; prefix?: (string)[] | undefined; version?: string | undefined; + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates. + * @public + */ + if_modified_since?: Date | undefined; + /** * Map representing the context. * Keys correspond to the names of the dimensions. @@ -782,25 +788,6 @@ export interface GetConfigOutput { dimensions: Record | undefined; version: string | undefined; last_modified: Date | undefined; - audit_id?: string | undefined; -} - -/** - * @public - */ -export interface GetConfigFastInput { - workspace_id: string | undefined; - org_id: string | undefined; -} - -/** - * @public - */ -export interface GetConfigFastOutput { - config?: __DocumentType | undefined; - version?: string | undefined; - last_modified?: Date | undefined; - audit_id?: string | undefined; } /** @@ -809,6 +796,11 @@ export interface GetConfigFastOutput { export interface GetConfigJsonInput { workspace_id: string | undefined; org_id: string | undefined; + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates. + * @public + */ + if_modified_since?: Date | undefined; } /** @@ -825,6 +817,11 @@ export interface GetConfigJsonOutput { export interface GetConfigTomlInput { workspace_id: string | undefined; org_id: string | undefined; + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates. + * @public + */ + if_modified_since?: Date | undefined; } /** @@ -1875,6 +1872,46 @@ export interface DiscardExperimentInput { change_reason: string | undefined; } +/** + * @public + */ +export interface GetExperimentConfigInput { + workspace_id: string | undefined; + org_id: string | undefined; + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates. + * @public + */ + if_modified_since?: Date | undefined; + + prefix?: (string)[] | undefined; + /** + * Map representing the context. + * Keys correspond to the names of the dimensions. + * @public + */ + context?: Record | undefined; + + /** + * Strategy to follow while filter items based on the context + * @public + */ + dimension_match_strategy?: DimensionMatchStrategy | undefined; +} + +/** + * @public + */ +export interface GetExperimentConfigOutput { + last_modified: Date | undefined; + experiments: (ExperimentResponse)[] | undefined; + /** + * A list of experiment group responses. + * @public + */ + experiment_groups: (ExperimentGroupResponse)[] | undefined; +} + /** * @public */ @@ -1931,6 +1968,12 @@ export interface ListExperimentGroupsInput { workspace_id: string | undefined; org_id: string | undefined; + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates. + * @public + */ + if_modified_since?: Date | undefined; + /** * Filter by experiment group name (exact match or substring, depending on backend implementation). * @public @@ -1966,6 +2009,19 @@ export interface ListExperimentGroupsInput { * @public */ group_type?: (GroupType)[] | undefined; + + /** + * Strategy to follow while filter items based on the context + * @public + */ + dimension_match_strategy?: DimensionMatchStrategy | undefined; + + /** + * Map representing the context. + * Keys correspond to the names of the dimensions. + * @public + */ + context?: Record | undefined; } /** @@ -1979,6 +2035,8 @@ export interface ListExperimentGroupsOutput { * @public */ data: (ExperimentGroupResponse)[] | undefined; + + last_modified: Date | undefined; } /** @@ -2054,6 +2112,12 @@ export interface ListExperimentInput { workspace_id: string | undefined; org_id: string | undefined; + /** + * While using this, 304 response is treated as error, which needs to be handled separately by checking the response code of the http response. This is required to make sure that clients can cache the response and avoid unnecessary calls when there are no updates. + * @public + */ + if_modified_since?: Date | undefined; + status?: (ExperimentStatusType)[] | undefined; from_date?: Date | undefined; to_date?: Date | undefined; @@ -2074,6 +2138,14 @@ export interface ListExperimentInput { * @public */ dimension_match_strategy?: DimensionMatchStrategy | undefined; + + prefix?: (string)[] | undefined; + /** + * Map representing the context. + * Keys correspond to the names of the dimensions. + * @public + */ + context?: Record | undefined; } /** @@ -2083,6 +2155,7 @@ export interface ListExperimentOutput { total_pages: number | undefined; total_items: number | undefined; data: (ExperimentResponse)[] | undefined; + last_modified: Date | undefined; } /** diff --git a/clients/javascript/sdk/src/protocols/Aws_restJson1.ts b/clients/javascript/sdk/src/protocols/Aws_restJson1.ts index 8c9b6f866..532f66bb0 100644 --- a/clients/javascript/sdk/src/protocols/Aws_restJson1.ts +++ b/clients/javascript/sdk/src/protocols/Aws_restJson1.ts @@ -107,10 +107,6 @@ import { GetConfigCommandInput, GetConfigCommandOutput, } from "../commands/GetConfigCommand"; -import { - GetConfigFastCommandInput, - GetConfigFastCommandOutput, -} from "../commands/GetConfigFastCommand"; import { GetConfigJsonCommandInput, GetConfigJsonCommandOutput, @@ -139,6 +135,10 @@ import { GetExperimentCommandInput, GetExperimentCommandOutput, } from "../commands/GetExperimentCommand"; +import { + GetExperimentConfigCommandInput, + GetExperimentConfigCommandOutput, +} from "../commands/GetExperimentConfigCommand"; import { GetExperimentGroupCommandInput, GetExperimentGroupCommandOutput, @@ -1094,6 +1094,7 @@ export const se_GetConfigCommand = async( 'content-type': 'application/json', [_xw]: input[_wi]!, [_xoi]: input[_oi]!, + [_ims_]: [() => isSerializableHeaderValue(input[_ims]), () => __serializeDateTime(input[_ims]!).toString()], }); b.bp("/config"); const query: any = map({ @@ -1111,26 +1112,6 @@ export const se_GetConfigCommand = async( return b.build(); } -/** - * serializeAws_restJson1GetConfigFastCommand - */ -export const se_GetConfigFastCommand = async( - input: GetConfigFastCommandInput, - context: __SerdeContext -): Promise<__HttpRequest> => { - const b = rb(input, context); - const headers: any = map({}, isSerializableHeaderValue, { - [_xw]: input[_wi]!, - [_xoi]: input[_oi]!, - }); - b.bp("/config/fast"); - let body: any; - b.m("GET") - .h(headers) - .b(body); - return b.build(); -} - /** * serializeAws_restJson1GetConfigJsonCommand */ @@ -1142,10 +1123,11 @@ export const se_GetConfigJsonCommand = async( const headers: any = map({}, isSerializableHeaderValue, { [_xw]: input[_wi]!, [_xoi]: input[_oi]!, + [_ims_]: [() => isSerializableHeaderValue(input[_ims]), () => __serializeDateTime(input[_ims]!).toString()], }); b.bp("/config/json"); let body: any; - b.m("GET") + b.m("POST") .h(headers) .b(body); return b.build(); @@ -1162,10 +1144,11 @@ export const se_GetConfigTomlCommand = async( const headers: any = map({}, isSerializableHeaderValue, { [_xw]: input[_wi]!, [_xoi]: input[_oi]!, + [_ims_]: [() => isSerializableHeaderValue(input[_ims]), () => __serializeDateTime(input[_ims]!).toString()], }); b.bp("/config/toml"); let body: any; - b.m("GET") + b.m("POST") .h(headers) .b(body); return b.build(); @@ -1284,6 +1267,36 @@ export const se_GetExperimentCommand = async( return b.build(); } +/** + * serializeAws_restJson1GetExperimentConfigCommand + */ +export const se_GetExperimentConfigCommand = async( + input: GetExperimentConfigCommandInput, + context: __SerdeContext +): Promise<__HttpRequest> => { + const b = rb(input, context); + const headers: any = map({}, isSerializableHeaderValue, { + 'content-type': 'application/json', + [_xw]: input[_wi]!, + [_xoi]: input[_oi]!, + [_ims_]: [() => isSerializableHeaderValue(input[_ims]), () => __serializeDateTime(input[_ims]!).toString()], + }); + b.bp("/experiment-config"); + const query: any = map({ + [_p]: [() => input.prefix !== void 0, () => ((input[_p]! || []))], + [_dms]: [,input[_dms]!], + }); + let body: any; + body = JSON.stringify(take(input, { + 'context': _ => se_ContextMap(_, context), + })); + b.m("POST") + .h(headers) + .q(query) + .b(body); + return b.build(); +} + /** * serializeAws_restJson1GetExperimentGroupCommand */ @@ -1711,10 +1724,12 @@ export const se_ListExperimentCommand = async( ): Promise<__HttpRequest> => { const b = rb(input, context); const headers: any = map({}, isSerializableHeaderValue, { + 'content-type': 'application/json', [_xw]: input[_wi]!, [_xoi]: input[_oi]!, + [_ims_]: [() => isSerializableHeaderValue(input[_ims]), () => __serializeDateTime(input[_ims]!).toString()], }); - b.bp("/experiments"); + b.bp("/experiments/list"); const query: any = map({ [_c]: [() => input.count !== void 0, () => (input[_c]!.toString())], [_pa]: [() => input.page !== void 0, () => (input[_pa]!.toString())], @@ -1730,9 +1745,13 @@ export const se_ListExperimentCommand = async( [_sb]: [,input[_sb]!], [_geo]: [() => input.global_experiments_only !== void 0, () => (input[_geo]!.toString())], [_dms]: [,input[_dms]!], + [_p]: [() => input.prefix !== void 0, () => ((input[_p]! || []))], }); let body: any; - b.m("GET") + body = JSON.stringify(take(input, { + 'context': _ => se_ContextMap(_, context), + })); + b.m("POST") .h(headers) .q(query) .b(body); @@ -1748,10 +1767,12 @@ export const se_ListExperimentGroupsCommand = async( ): Promise<__HttpRequest> => { const b = rb(input, context); const headers: any = map({}, isSerializableHeaderValue, { + 'content-type': 'application/json', [_xw]: input[_wi]!, [_xoi]: input[_oi]!, + [_ims_]: [() => isSerializableHeaderValue(input[_ims]), () => __serializeDateTime(input[_ims]!).toString()], }); - b.bp("/experiment-groups"); + b.bp("/experiment-groups/list"); const query: any = map({ [_c]: [() => input.count !== void 0, () => (input[_c]!.toString())], [_pa]: [() => input.page !== void 0, () => (input[_pa]!.toString())], @@ -1762,9 +1783,13 @@ export const se_ListExperimentGroupsCommand = async( [_so]: [,input[_so]!], [_sb]: [,input[_sb]!], [_gt]: [() => input.group_type !== void 0, () => ((input[_gt]! || []))], + [_dms]: [,input[_dms]!], }); let body: any; - b.m("GET") + body = JSON.stringify(take(input, { + 'context': _ => se_ContextMap(_, context), + })); + b.m("POST") .h(headers) .q(query) .b(body); @@ -3358,7 +3383,6 @@ export const de_GetConfigCommand = async( $metadata: deserializeMetadata(output), [_v]: [, output.headers[_xcv]], [_lm_]: [() => void 0 !== output.headers[_lm], () => __expectNonNull(__parseRfc3339DateTimeWithOffset(output.headers[_lm]))], - [_ai]: [, output.headers[_xai]], }); const data: Record = __expectNonNull((__expectObject(await parseBody(output.body, context))), "body"); const doc = take(data, { @@ -3371,28 +3395,6 @@ export const de_GetConfigCommand = async( return contents; } -/** - * deserializeAws_restJson1GetConfigFastCommand - */ -export const de_GetConfigFastCommand = async( - output: __HttpResponse, - context: __SerdeContext -): Promise => { - if (output.statusCode !== 200 && output.statusCode >= 300) { - return de_CommandError(output, context); - } - const contents: any = map({ - $metadata: deserializeMetadata(output), - [_v]: [, output.headers[_xcv]], - [_lm_]: [() => void 0 !== output.headers[_lm], () => __expectNonNull(__parseRfc3339DateTimeWithOffset(output.headers[_lm]))], - [_ai]: [, output.headers[_xai]], - }); - const data: any = await collectBodyString(output.body, context); - contents.config = data; - contents.config = JSON.parse(data); - return contents; -} - /** * deserializeAws_restJson1GetConfigJsonCommand */ @@ -3598,6 +3600,29 @@ export const de_GetExperimentCommand = async( return contents; } +/** + * deserializeAws_restJson1GetExperimentConfigCommand + */ +export const de_GetExperimentConfigCommand = async( + output: __HttpResponse, + context: __SerdeContext +): Promise => { + if (output.statusCode !== 200 && output.statusCode >= 300) { + return de_CommandError(output, context); + } + const contents: any = map({ + $metadata: deserializeMetadata(output), + [_lm_]: [() => void 0 !== output.headers[_lm], () => __expectNonNull(__parseRfc3339DateTimeWithOffset(output.headers[_lm]))], + }); + const data: Record = __expectNonNull((__expectObject(await parseBody(output.body, context))), "body"); + const doc = take(data, { + 'experiment_groups': _ => de_ExperimentGroupList(_, context), + 'experiments': _ => de_ExperimentList(_, context), + }); + Object.assign(contents, doc); + return contents; +} + /** * deserializeAws_restJson1GetExperimentGroupCommand */ @@ -4085,6 +4110,7 @@ export const de_ListExperimentCommand = async( } const contents: any = map({ $metadata: deserializeMetadata(output), + [_lm_]: [() => void 0 !== output.headers[_lm], () => __expectNonNull(__parseRfc3339DateTimeWithOffset(output.headers[_lm]))], }); const data: Record = __expectNonNull((__expectObject(await parseBody(output.body, context))), "body"); const doc = take(data, { @@ -4108,6 +4134,7 @@ export const de_ListExperimentGroupsCommand = async( } const contents: any = map({ $metadata: deserializeMetadata(output), + [_lm_]: [() => void 0 !== output.headers[_lm], () => __expectNonNull(__parseRfc3339DateTimeWithOffset(output.headers[_lm]))], }); const data: Record = __expectNonNull((__expectObject(await parseBody(output.body, context))), "body"); const doc = take(data, { @@ -6191,6 +6218,8 @@ const de_CommandError = async( const _geo = "global_experiments_only"; const _gt = "group_type"; const _i = "identifier"; + const _ims = "if_modified_since"; + const _ims_ = "if-modified-since"; const _lm = "last-modified"; const _lm_ = "last_modified"; const _lmb = "last_modified_by"; diff --git a/clients/python/sdk/superposition_sdk/_private/schemas.py b/clients/python/sdk/superposition_sdk/_private/schemas.py index 71f0bb603..d1801435a 100644 --- a/clients/python/sdk/superposition_sdk/_private/schemas.py +++ b/clients/python/sdk/superposition_sdk/_private/schemas.py @@ -1717,9 +1717,19 @@ ], }, + "if_modified_since": { + "target": DATE_TIME, + "index": 4, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="if-modified-since"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + "context": { "target": CONTEXT_MAP, - "index": 4, + "index": 5, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), @@ -2018,16 +2028,6 @@ ], }, - "audit_id": { - "target": STRING, - "index": 6, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-audit-id"), - Trait.new(id=ShapeID("smithy.api#notProperty")), - - ], - }, - } ) @@ -2047,8 +2047,8 @@ ) -GET_CONFIG_FAST_INPUT = Schema.collection( - id=ShapeID("io.superposition#GetConfigFastInput"), +GET_CONFIG_JSON_INPUT = Schema.collection( + id=ShapeID("io.superposition#GetConfigJsonInput"), traits=[ Trait.new(id=ShapeID("smithy.api#input")), @@ -2075,50 +2075,11 @@ ], }, - } -) - -GET_CONFIG_FAST_OUTPUT = Schema.collection( - id=ShapeID("io.superposition#GetConfigFastOutput"), - - traits=[ - Trait.new(id=ShapeID("smithy.api#output")), - - ], - members={ - "config": { - "target": DOCUMENT, - "index": 0, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpPayload")), - - ], - }, - - "version": { - "target": STRING, - "index": 1, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-config-version"), - Trait.new(id=ShapeID("smithy.api#notProperty")), - - ], - }, - - "last_modified": { + "if_modified_since": { "target": DATE_TIME, "index": 2, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="last-modified"), - - ], - }, - - "audit_id": { - "target": STRING, - "index": 3, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-audit-id"), + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="if-modified-since"), Trait.new(id=ShapeID("smithy.api#notProperty")), ], @@ -2127,53 +2088,6 @@ } ) -GET_CONFIG_FAST = Schema( - id=ShapeID("io.superposition#GetConfigFast"), - shape_type=ShapeType.OPERATION, - traits=[ - Trait.new(id=ShapeID("smithy.api#tags"), value=( - "Configuration Management", - )), - Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({ - "method": "GET", - "uri": "/config/fast", - })), - - ], - -) - -GET_CONFIG_JSON_INPUT = Schema.collection( - id=ShapeID("io.superposition#GetConfigJsonInput"), - - traits=[ - Trait.new(id=ShapeID("smithy.api#input")), - - ], - members={ - "workspace_id": { - "target": STRING, - "index": 0, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-workspace"), - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "org_id": { - "target": STRING, - "index": 1, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-org-id"), - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - } -) - GET_CONFIG_JSON_OUTPUT = Schema.collection( id=ShapeID("io.superposition#GetConfigJsonOutput"), @@ -2213,7 +2127,7 @@ "Configuration Management", )), Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({ - "method": "GET", + "method": "POST", "uri": "/config/json", })), Trait.new(id=ShapeID("smithy.api#readonly")), @@ -2250,6 +2164,16 @@ ], }, + "if_modified_since": { + "target": DATE_TIME, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="if-modified-since"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + } ) @@ -2292,7 +2216,7 @@ "Configuration Management", )), Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({ - "method": "GET", + "method": "POST", "uri": "/config/toml", })), Trait.new(id=ShapeID("smithy.api#readonly")), @@ -9339,55 +9263,9 @@ ) -GET_EXPERIMENT_GROUP_INPUT = Schema.collection( - id=ShapeID("io.superposition#GetExperimentGroupInput"), - - traits=[ - Trait.new(id=ShapeID("smithy.api#input")), - - ], - members={ - "workspace_id": { - "target": STRING, - "index": 0, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-workspace"), - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "org_id": { - "target": STRING, - "index": 1, - "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-org-id"), - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "id": { - "target": STRING, - "index": 2, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - Trait.new(id=ShapeID("smithy.api#httpLabel")), - - ], - }, - - } -) - -GET_EXPERIMENT_GROUP_OUTPUT = Schema.collection( - id=ShapeID("io.superposition#GetExperimentGroupOutput"), - - traits=[ - Trait.new(id=ShapeID("smithy.synthetic#originalShapeId"), value="io.superposition#ExperimentGroupResponse"), - Trait.new(id=ShapeID("smithy.api#output")), +EXPERIMENT_GROUP_RESPONSE = Schema.collection( + id=ShapeID("io.superposition#ExperimentGroupResponse"), - ], members={ "id": { "target": STRING, @@ -9522,176 +9400,256 @@ } ) -GET_EXPERIMENT_GROUP = Schema( - id=ShapeID("io.superposition#GetExperimentGroup"), - shape_type=ShapeType.OPERATION, - traits=[ - Trait.new(id=ShapeID("smithy.api#tags"), value=( - "Experiment Groups", - )), - Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({ - "method": "GET", - "uri": "/experiment-groups/{id}", - })), - Trait.new(id=ShapeID("smithy.api#readonly")), - - ], - -) - -GROUP_TYPE_LIST = Schema.collection( - id=ShapeID("io.superposition#GroupTypeList"), +EXPERIMENT_GROUP_LIST = Schema.collection( + id=ShapeID("io.superposition#ExperimentGroupList"), shape_type=ShapeType.LIST, members={ "member": { - "target": GROUP_TYPE, + "target": EXPERIMENT_GROUP_RESPONSE, "index": 0, }, } ) -EXPERIMENT_GROUP_SORT_ON = Schema.collection( - id=ShapeID("io.superposition#ExperimentGroupSortOn"), - shape_type=ShapeType.ENUM, +EXPERIMENT_RESPONSE = Schema.collection( + id=ShapeID("io.superposition#ExperimentResponse"), + members={ - "NAME": { - "target": UNIT, + "id": { + "target": STRING, "index": 0, "traits": [ - Trait.new(id=ShapeID("smithy.api#enumValue"), value="name"), + Trait.new(id=ShapeID("smithy.api#required")), ], }, - "CREATED_AT": { - "target": UNIT, + "created_at": { + "target": DATE_TIME, "index": 1, "traits": [ - Trait.new(id=ShapeID("smithy.api#enumValue"), value="created_at"), + Trait.new(id=ShapeID("smithy.api#required")), ], }, - "LAST_MODIFIED_AT": { - "target": UNIT, + "created_by": { + "target": STRING, "index": 2, "traits": [ - Trait.new(id=ShapeID("smithy.api#enumValue"), value="last_modified_at"), + Trait.new(id=ShapeID("smithy.api#required")), ], }, - } -) - -LIST_EXPERIMENT_GROUPS_INPUT = Schema.collection( - id=ShapeID("io.superposition#ListExperimentGroupsInput"), - - traits=[ - Trait.new(id=ShapeID("smithy.api#input")), - - ], - members={ - "count": { - "target": INTEGER, - "index": 0, + "last_modified": { + "target": DATE_TIME, + "index": 3, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpQuery"), value="count"), + Trait.new(id=ShapeID("smithy.api#required")), ], }, - "page": { - "target": INTEGER, - "index": 1, + "name": { + "target": STRING, + "index": 4, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpQuery"), value="page"), + Trait.new(id=ShapeID("smithy.api#required")), ], }, - "all": { - "target": BOOLEAN, - "index": 2, + "experiment_type": { + "target": EXPERIMENT_TYPE, + "index": 5, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpQuery"), value="all"), + Trait.new(id=ShapeID("smithy.api#required")), ], }, - "workspace_id": { - "target": STRING, - "index": 3, + "override_keys": { + "target": LIST_OVERRIDE_KEYS, + "index": 6, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-workspace"), Trait.new(id=ShapeID("smithy.api#required")), ], }, - "org_id": { + "status": { + "target": EXPERIMENT_STATUS_TYPE, + "index": 7, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "traffic_percentage": { + "target": INTEGER, + "index": 8, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "context": { + "target": CONDITION, + "index": 9, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "variants": { + "target": LIST_VARIANT, + "index": 10, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "last_modified_by": { "target": STRING, - "index": 4, + "index": 11, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-org-id"), Trait.new(id=ShapeID("smithy.api#required")), ], }, - "name": { + "chosen_variant": { "target": STRING, - "index": 5, + "index": 12, + }, + + "description": { + "target": STRING, + "index": 13, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpQuery"), value="name"), + Trait.new(id=ShapeID("smithy.api#required")), ], }, - "created_by": { + "change_reason": { "target": STRING, - "index": 6, + "index": 14, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpQuery"), value="created_by"), + Trait.new(id=ShapeID("smithy.api#required")), ], }, - "last_modified_by": { + "started_at": { + "target": DATE_TIME, + "index": 15, + }, + + "started_by": { "target": STRING, - "index": 7, + "index": 16, + }, + + "metrics_url": { + "target": STRING, + "index": 17, + }, + + "metrics": { + "target": DOCUMENT, + "index": 18, + }, + + "experiment_group_id": { + "target": STRING, + "index": 19, + }, + + } +) + +EXPERIMENT_LIST = Schema.collection( + id=ShapeID("io.superposition#ExperimentList"), + shape_type=ShapeType.LIST, + members={ + "member": { + "target": EXPERIMENT_RESPONSE, + "index": 0, + }, + + } +) + +GET_EXPERIMENT_CONFIG_INPUT = Schema.collection( + id=ShapeID("io.superposition#GetExperimentConfigInput"), + + traits=[ + Trait.new(id=ShapeID("smithy.api#input")), + + ], + members={ + "workspace_id": { + "target": STRING, + "index": 0, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpQuery"), value="last_modified_by"), + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-workspace"), + Trait.new(id=ShapeID("smithy.api#required")), ], }, - "sort_on": { - "target": EXPERIMENT_GROUP_SORT_ON, - "index": 8, + "org_id": { + "target": STRING, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-org-id"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "if_modified_since": { + "target": DATE_TIME, + "index": 2, "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="if-modified-since"), Trait.new(id=ShapeID("smithy.api#notProperty")), - Trait.new(id=ShapeID("smithy.api#httpQuery"), value="sort_on"), ], }, - "sort_by": { - "target": SORT_BY, - "index": 9, + "prefix": { + "target": STRING_LIST, + "index": 3, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), - Trait.new(id=ShapeID("smithy.api#httpQuery"), value="sort_by"), + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="prefix"), ], }, - "group_type": { - "target": GROUP_TYPE_LIST, - "index": 10, + "context": { + "target": CONTEXT_MAP, + "index": 4, "traits": [ - Trait.new(id=ShapeID("smithy.api#httpQuery"), value="group_type"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "dimension_match_strategy": { + "target": DIMENSION_MATCH_STRATEGY, + "index": 5, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="dimension_match_strategy"), ], }, @@ -9699,9 +9657,110 @@ } ) -EXPERIMENT_GROUP_RESPONSE = Schema.collection( - id=ShapeID("io.superposition#ExperimentGroupResponse"), +GET_EXPERIMENT_CONFIG_OUTPUT = Schema.collection( + id=ShapeID("io.superposition#GetExperimentConfigOutput"), + + traits=[ + Trait.new(id=ShapeID("smithy.api#output")), + + ], + members={ + "last_modified": { + "target": DATE_TIME, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="last-modified"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "experiments": { + "target": EXPERIMENT_LIST, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "experiment_groups": { + "target": EXPERIMENT_GROUP_LIST, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + } +) + +GET_EXPERIMENT_CONFIG = Schema( + id=ShapeID("io.superposition#GetExperimentConfig"), + shape_type=ShapeType.OPERATION, + traits=[ + Trait.new(id=ShapeID("smithy.api#tags"), value=( + "Experiment Config", + )), + Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({ + "method": "POST", + "uri": "/experiment-config", + })), + + ], + +) + +GET_EXPERIMENT_GROUP_INPUT = Schema.collection( + id=ShapeID("io.superposition#GetExperimentGroupInput"), + + traits=[ + Trait.new(id=ShapeID("smithy.api#input")), + + ], + members={ + "workspace_id": { + "target": STRING, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-workspace"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "org_id": { + "target": STRING, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-org-id"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "id": { + "target": STRING, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#required")), + Trait.new(id=ShapeID("smithy.api#httpLabel")), + ], + }, + + } +) + +GET_EXPERIMENT_GROUP_OUTPUT = Schema.collection( + id=ShapeID("io.superposition#GetExperimentGroupOutput"), + + traits=[ + Trait.new(id=ShapeID("smithy.synthetic#originalShapeId"), value="io.superposition#ExperimentGroupResponse"), + Trait.new(id=ShapeID("smithy.api#output")), + + ], members={ "id": { "target": STRING, @@ -9836,18 +9895,212 @@ } ) -EXPERIMENT_GROUP_LIST = Schema.collection( - id=ShapeID("io.superposition#ExperimentGroupList"), +GET_EXPERIMENT_GROUP = Schema( + id=ShapeID("io.superposition#GetExperimentGroup"), + shape_type=ShapeType.OPERATION, + traits=[ + Trait.new(id=ShapeID("smithy.api#tags"), value=( + "Experiment Groups", + )), + Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({ + "method": "GET", + "uri": "/experiment-groups/{id}", + })), + Trait.new(id=ShapeID("smithy.api#readonly")), + + ], + +) + +GROUP_TYPE_LIST = Schema.collection( + id=ShapeID("io.superposition#GroupTypeList"), shape_type=ShapeType.LIST, members={ "member": { - "target": EXPERIMENT_GROUP_RESPONSE, + "target": GROUP_TYPE, "index": 0, }, } ) +EXPERIMENT_GROUP_SORT_ON = Schema.collection( + id=ShapeID("io.superposition#ExperimentGroupSortOn"), + shape_type=ShapeType.ENUM, + members={ + "NAME": { + "target": UNIT, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#enumValue"), value="name"), + + ], + }, + + "CREATED_AT": { + "target": UNIT, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#enumValue"), value="created_at"), + + ], + }, + + "LAST_MODIFIED_AT": { + "target": UNIT, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#enumValue"), value="last_modified_at"), + + ], + }, + + } +) + +LIST_EXPERIMENT_GROUPS_INPUT = Schema.collection( + id=ShapeID("io.superposition#ListExperimentGroupsInput"), + + traits=[ + Trait.new(id=ShapeID("smithy.api#input")), + + ], + members={ + "count": { + "target": INTEGER, + "index": 0, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="count"), + + ], + }, + + "page": { + "target": INTEGER, + "index": 1, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="page"), + + ], + }, + + "all": { + "target": BOOLEAN, + "index": 2, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="all"), + + ], + }, + + "workspace_id": { + "target": STRING, + "index": 3, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-workspace"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "org_id": { + "target": STRING, + "index": 4, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="x-org-id"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + + "if_modified_since": { + "target": DATE_TIME, + "index": 5, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="if-modified-since"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + "name": { + "target": STRING, + "index": 6, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="name"), + + ], + }, + + "created_by": { + "target": STRING, + "index": 7, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="created_by"), + + ], + }, + + "last_modified_by": { + "target": STRING, + "index": 8, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="last_modified_by"), + + ], + }, + + "sort_on": { + "target": EXPERIMENT_GROUP_SORT_ON, + "index": 9, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="sort_on"), + + ], + }, + + "sort_by": { + "target": SORT_BY, + "index": 10, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="sort_by"), + + ], + }, + + "group_type": { + "target": GROUP_TYPE_LIST, + "index": 11, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="group_type"), + + ], + }, + + "dimension_match_strategy": { + "target": DIMENSION_MATCH_STRATEGY, + "index": 12, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="dimension_match_strategy"), + + ], + }, + + "context": { + "target": CONTEXT_MAP, + "index": 13, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + + } +) + LIST_EXPERIMENT_GROUPS_OUTPUT = Schema.collection( id=ShapeID("io.superposition#ListExperimentGroupsOutput"), @@ -9883,6 +10136,16 @@ ], }, + "last_modified": { + "target": DATE_TIME, + "index": 3, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="last-modified"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + } ) @@ -9894,10 +10157,9 @@ "Experiment Groups", )), Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({ - "method": "GET", - "uri": "/experiment-groups", + "method": "POST", + "uri": "/experiment-groups/list", })), - Trait.new(id=ShapeID("smithy.api#readonly")), ], @@ -10348,181 +10610,6 @@ ) -EXPERIMENT_RESPONSE = Schema.collection( - id=ShapeID("io.superposition#ExperimentResponse"), - - members={ - "id": { - "target": STRING, - "index": 0, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "created_at": { - "target": DATE_TIME, - "index": 1, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "created_by": { - "target": STRING, - "index": 2, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "last_modified": { - "target": DATE_TIME, - "index": 3, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "name": { - "target": STRING, - "index": 4, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "experiment_type": { - "target": EXPERIMENT_TYPE, - "index": 5, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "override_keys": { - "target": LIST_OVERRIDE_KEYS, - "index": 6, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "status": { - "target": EXPERIMENT_STATUS_TYPE, - "index": 7, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "traffic_percentage": { - "target": INTEGER, - "index": 8, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "context": { - "target": CONDITION, - "index": 9, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "variants": { - "target": LIST_VARIANT, - "index": 10, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "last_modified_by": { - "target": STRING, - "index": 11, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "chosen_variant": { - "target": STRING, - "index": 12, - }, - - "description": { - "target": STRING, - "index": 13, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "change_reason": { - "target": STRING, - "index": 14, - "traits": [ - Trait.new(id=ShapeID("smithy.api#required")), - - ], - }, - - "started_at": { - "target": DATE_TIME, - "index": 15, - }, - - "started_by": { - "target": STRING, - "index": 16, - }, - - "metrics_url": { - "target": STRING, - "index": 17, - }, - - "metrics": { - "target": DOCUMENT, - "index": 18, - }, - - "experiment_group_id": { - "target": STRING, - "index": 19, - }, - - } -) - -EXPERIMENT_LIST = Schema.collection( - id=ShapeID("io.superposition#ExperimentList"), - shape_type=ShapeType.LIST, - members={ - "member": { - "target": EXPERIMENT_RESPONSE, - "index": 0, - }, - - } -) - GET_EXPERIMENT_INPUT = Schema.collection( id=ShapeID("io.superposition#GetExperimentInput"), @@ -10841,9 +10928,19 @@ ], }, + "if_modified_since": { + "target": DATE_TIME, + "index": 5, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="if-modified-since"), + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + "status": { "target": EXPERIMENT_STATUS_TYPE_LIST, - "index": 5, + "index": 6, "traits": [ Trait.new(id=ShapeID("smithy.api#httpQuery"), value="status"), @@ -10852,7 +10949,7 @@ "from_date": { "target": DATE_TIME, - "index": 6, + "index": 7, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#httpQuery"), value="from_date"), @@ -10862,7 +10959,7 @@ "to_date": { "target": DATE_TIME, - "index": 7, + "index": 8, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#httpQuery"), value="to_date"), @@ -10872,7 +10969,7 @@ "experiment_name": { "target": STRING, - "index": 8, + "index": 9, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#httpQuery"), value="experiment_name"), @@ -10882,7 +10979,7 @@ "experiment_ids": { "target": STRING_LIST, - "index": 9, + "index": 10, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#httpQuery"), value="experiment_ids"), @@ -10892,7 +10989,7 @@ "experiment_group_ids": { "target": STRING_LIST, - "index": 10, + "index": 11, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#httpQuery"), value="experiment_group_ids"), @@ -10902,7 +10999,7 @@ "created_by": { "target": STRING_LIST, - "index": 11, + "index": 12, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#httpQuery"), value="created_by"), @@ -10912,7 +11009,7 @@ "sort_on": { "target": EXPERIMENT_SORT_ON, - "index": 12, + "index": 13, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#httpQuery"), value="sort_on"), @@ -10922,7 +11019,7 @@ "sort_by": { "target": SORT_BY, - "index": 13, + "index": 14, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#httpQuery"), value="sort_by"), @@ -10932,7 +11029,7 @@ "global_experiments_only": { "target": BOOLEAN, - "index": 14, + "index": 15, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#httpQuery"), value="global_experiments_only"), @@ -10942,7 +11039,7 @@ "dimension_match_strategy": { "target": DIMENSION_MATCH_STRATEGY, - "index": 15, + "index": 16, "traits": [ Trait.new(id=ShapeID("smithy.api#notProperty")), Trait.new(id=ShapeID("smithy.api#httpQuery"), value="dimension_match_strategy"), @@ -10950,6 +11047,25 @@ ], }, + "prefix": { + "target": STRING_LIST, + "index": 17, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + Trait.new(id=ShapeID("smithy.api#httpQuery"), value="prefix"), + + ], + }, + + "context": { + "target": CONTEXT_MAP, + "index": 18, + "traits": [ + Trait.new(id=ShapeID("smithy.api#notProperty")), + + ], + }, + } ) @@ -10988,6 +11104,16 @@ ], }, + "last_modified": { + "target": DATE_TIME, + "index": 3, + "traits": [ + Trait.new(id=ShapeID("smithy.api#httpHeader"), value="last-modified"), + Trait.new(id=ShapeID("smithy.api#required")), + + ], + }, + } ) @@ -10999,10 +11125,9 @@ "Experimentation", )), Trait.new(id=ShapeID("smithy.api#http"), value=MappingProxyType({ - "method": "GET", - "uri": "/experiments", + "method": "POST", + "uri": "/experiments/list", })), - Trait.new(id=ShapeID("smithy.api#readonly")), ], diff --git a/clients/python/sdk/superposition_sdk/client.py b/clients/python/sdk/superposition_sdk/client.py index f5afc50a3..17c6836cc 100644 --- a/clients/python/sdk/superposition_sdk/client.py +++ b/clients/python/sdk/superposition_sdk/client.py @@ -61,7 +61,6 @@ _deserialize_delete_webhook, _deserialize_discard_experiment, _deserialize_get_config, - _deserialize_get_config_fast, _deserialize_get_config_json, _deserialize_get_config_toml, _deserialize_get_context, @@ -69,6 +68,7 @@ _deserialize_get_default_config, _deserialize_get_dimension, _deserialize_get_experiment, + _deserialize_get_experiment_config, _deserialize_get_experiment_group, _deserialize_get_function, _deserialize_get_organisation, @@ -200,7 +200,6 @@ DiscardExperimentInput, DiscardExperimentOutput, GET_CONFIG, - GET_CONFIG_FAST, GET_CONFIG_JSON, GET_CONFIG_TOML, GET_CONTEXT, @@ -208,6 +207,7 @@ GET_DEFAULT_CONFIG, GET_DIMENSION, GET_EXPERIMENT, + GET_EXPERIMENT_CONFIG, GET_EXPERIMENT_GROUP, GET_FUNCTION, GET_ORGANISATION, @@ -221,8 +221,6 @@ GET_WEBHOOK, GET_WEBHOOK_BY_EVENT, GET_WORKSPACE, - GetConfigFastInput, - GetConfigFastOutput, GetConfigInput, GetConfigJsonInput, GetConfigJsonOutput, @@ -237,6 +235,8 @@ GetDefaultConfigOutput, GetDimensionInput, GetDimensionOutput, + GetExperimentConfigInput, + GetExperimentConfigOutput, GetExperimentGroupInput, GetExperimentGroupOutput, GetExperimentInput, @@ -406,7 +406,6 @@ _serialize_delete_webhook, _serialize_discard_experiment, _serialize_get_config, - _serialize_get_config_fast, _serialize_get_config_json, _serialize_get_config_toml, _serialize_get_context, @@ -414,6 +413,7 @@ _serialize_get_default_config, _serialize_get_dimension, _serialize_get_experiment, + _serialize_get_experiment_config, _serialize_get_experiment_group, _serialize_get_function, _serialize_get_organisation, @@ -1223,32 +1223,6 @@ async def get_config(self, input: GetConfigInput, plugins: list[Plugin] | None = operation=GET_CONFIG, ) - async def get_config_fast(self, input: GetConfigFastInput, plugins: list[Plugin] | None = None) -> GetConfigFastOutput: - """ - Retrieves the latest config with no processing for high-performance access. - - :param input: The operation's input. - - :param plugins: A list of callables that modify the configuration dynamically. - Changes made by these plugins only apply for the duration of the operation - execution and will not affect any other operation invocations. - - """ - operation_plugins: list[Plugin] = [ - - ] - if plugins: - operation_plugins.extend(plugins) - - return await self._execute_operation( - input=input, - plugins=operation_plugins, - serialize=_serialize_get_config_fast, - deserialize=_deserialize_get_config_fast, - config=self._config, - operation=GET_CONFIG_FAST, - ) - async def get_config_json(self, input: GetConfigJsonInput, plugins: list[Plugin] | None = None) -> GetConfigJsonOutput: """ Retrieves the full config in JSON format, including default configs with @@ -1440,6 +1414,34 @@ async def get_experiment(self, input: GetExperimentInput, plugins: list[Plugin] operation=GET_EXPERIMENT, ) + async def get_experiment_config(self, input: GetExperimentConfigInput, plugins: list[Plugin] | None = None) -> GetExperimentConfigOutput: + """ + Retrieves the experiment configuration for a given workspace and organization. + The response includes details of all experiment groups and experiments that + match the specified filters. + + :param input: The operation's input. + + :param plugins: A list of callables that modify the configuration dynamically. + Changes made by these plugins only apply for the duration of the operation + execution and will not affect any other operation invocations. + + """ + operation_plugins: list[Plugin] = [ + + ] + if plugins: + operation_plugins.extend(plugins) + + return await self._execute_operation( + input=input, + plugins=operation_plugins, + serialize=_serialize_get_experiment_config, + deserialize=_deserialize_get_experiment_config, + config=self._config, + operation=GET_EXPERIMENT_CONFIG, + ) + async def get_experiment_group(self, input: GetExperimentGroupInput, plugins: list[Plugin] | None = None) -> GetExperimentGroupOutput: """ Retrieves an existing experiment group by its ID. diff --git a/clients/python/sdk/superposition_sdk/config.py b/clients/python/sdk/superposition_sdk/config.py index 03e884343..504703be7 100644 --- a/clients/python/sdk/superposition_sdk/config.py +++ b/clients/python/sdk/superposition_sdk/config.py @@ -68,8 +68,6 @@ DeleteWebhookOutput, DiscardExperimentInput, DiscardExperimentOutput, - GetConfigFastInput, - GetConfigFastOutput, GetConfigInput, GetConfigJsonInput, GetConfigJsonOutput, @@ -84,6 +82,8 @@ GetDefaultConfigOutput, GetDimensionInput, GetDimensionOutput, + GetExperimentConfigInput, + GetExperimentConfigOutput, GetExperimentGroupInput, GetExperimentGroupOutput, GetExperimentInput, @@ -189,7 +189,7 @@ ) -_ServiceInterceptor = Union[Interceptor[AddMembersToGroupInput, AddMembersToGroupOutput, Any, Any], Interceptor[ApplicableVariantsInput, ApplicableVariantsOutput, Any, Any], Interceptor[BulkOperationInput, BulkOperationOutput, Any, Any], Interceptor[ConcludeExperimentInput, ConcludeExperimentOutput, Any, Any], Interceptor[CreateContextInput, CreateContextOutput, Any, Any], Interceptor[CreateDefaultConfigInput, CreateDefaultConfigOutput, Any, Any], Interceptor[CreateDimensionInput, CreateDimensionOutput, Any, Any], Interceptor[CreateExperimentInput, CreateExperimentOutput, Any, Any], Interceptor[CreateExperimentGroupInput, CreateExperimentGroupOutput, Any, Any], Interceptor[CreateFunctionInput, CreateFunctionOutput, Any, Any], Interceptor[CreateOrganisationInput, CreateOrganisationOutput, Any, Any], Interceptor[CreateSecretInput, CreateSecretOutput, Any, Any], Interceptor[CreateTypeTemplatesInput, CreateTypeTemplatesOutput, Any, Any], Interceptor[CreateVariableInput, CreateVariableOutput, Any, Any], Interceptor[CreateWebhookInput, CreateWebhookOutput, Any, Any], Interceptor[CreateWorkspaceInput, CreateWorkspaceOutput, Any, Any], Interceptor[DeleteContextInput, DeleteContextOutput, Any, Any], Interceptor[DeleteDefaultConfigInput, DeleteDefaultConfigOutput, Any, Any], Interceptor[DeleteDimensionInput, DeleteDimensionOutput, Any, Any], Interceptor[DeleteExperimentGroupInput, DeleteExperimentGroupOutput, Any, Any], Interceptor[DeleteFunctionInput, DeleteFunctionOutput, Any, Any], Interceptor[DeleteSecretInput, DeleteSecretOutput, Any, Any], Interceptor[DeleteTypeTemplatesInput, DeleteTypeTemplatesOutput, Any, Any], Interceptor[DeleteVariableInput, DeleteVariableOutput, Any, Any], Interceptor[DeleteWebhookInput, DeleteWebhookOutput, Any, Any], Interceptor[DiscardExperimentInput, DiscardExperimentOutput, Any, Any], Interceptor[GetConfigInput, GetConfigOutput, Any, Any], Interceptor[GetConfigFastInput, GetConfigFastOutput, Any, Any], Interceptor[GetConfigJsonInput, GetConfigJsonOutput, Any, Any], Interceptor[GetConfigTomlInput, GetConfigTomlOutput, Any, Any], Interceptor[GetContextInput, GetContextOutput, Any, Any], Interceptor[GetContextFromConditionInput, GetContextFromConditionOutput, Any, Any], Interceptor[GetDefaultConfigInput, GetDefaultConfigOutput, Any, Any], Interceptor[GetDimensionInput, GetDimensionOutput, Any, Any], Interceptor[GetExperimentInput, GetExperimentOutput, Any, Any], Interceptor[GetExperimentGroupInput, GetExperimentGroupOutput, Any, Any], Interceptor[GetFunctionInput, GetFunctionOutput, Any, Any], Interceptor[GetOrganisationInput, GetOrganisationOutput, Any, Any], Interceptor[GetResolvedConfigInput, GetResolvedConfigOutput, Any, Any], Interceptor[GetResolvedConfigWithIdentifierInput, GetResolvedConfigWithIdentifierOutput, Any, Any], Interceptor[GetSecretInput, GetSecretOutput, Any, Any], Interceptor[GetTypeTemplateInput, GetTypeTemplateOutput, Any, Any], Interceptor[GetTypeTemplatesListInput, GetTypeTemplatesListOutput, Any, Any], Interceptor[GetVariableInput, GetVariableOutput, Any, Any], Interceptor[GetVersionInput, GetVersionOutput, Any, Any], Interceptor[GetWebhookInput, GetWebhookOutput, Any, Any], Interceptor[GetWebhookByEventInput, GetWebhookByEventOutput, Any, Any], Interceptor[GetWorkspaceInput, GetWorkspaceOutput, Any, Any], Interceptor[ListAuditLogsInput, ListAuditLogsOutput, Any, Any], Interceptor[ListContextsInput, ListContextsOutput, Any, Any], Interceptor[ListDefaultConfigsInput, ListDefaultConfigsOutput, Any, Any], Interceptor[ListDimensionsInput, ListDimensionsOutput, Any, Any], Interceptor[ListExperimentInput, ListExperimentOutput, Any, Any], Interceptor[ListExperimentGroupsInput, ListExperimentGroupsOutput, Any, Any], Interceptor[ListFunctionInput, ListFunctionOutput, Any, Any], Interceptor[ListOrganisationInput, ListOrganisationOutput, Any, Any], Interceptor[ListSecretsInput, ListSecretsOutput, Any, Any], Interceptor[ListVariablesInput, ListVariablesOutput, Any, Any], Interceptor[ListVersionsInput, ListVersionsOutput, Any, Any], Interceptor[ListWebhookInput, ListWebhookOutput, Any, Any], Interceptor[ListWorkspaceInput, ListWorkspaceOutput, Any, Any], Interceptor[MigrateWorkspaceSchemaInput, MigrateWorkspaceSchemaOutput, Any, Any], Interceptor[MoveContextInput, MoveContextOutput, Any, Any], Interceptor[PauseExperimentInput, PauseExperimentOutput, Any, Any], Interceptor[PublishInput, PublishOutput, Any, Any], Interceptor[RampExperimentInput, RampExperimentOutput, Any, Any], Interceptor[RemoveMembersFromGroupInput, RemoveMembersFromGroupOutput, Any, Any], Interceptor[ResumeExperimentInput, ResumeExperimentOutput, Any, Any], Interceptor[RotateMasterEncryptionKeyInput, RotateMasterEncryptionKeyOutput, Any, Any], Interceptor[RotateWorkspaceEncryptionKeyInput, RotateWorkspaceEncryptionKeyOutput, Any, Any], Interceptor[TestInput, TestOutput, Any, Any], Interceptor[UpdateDefaultConfigInput, UpdateDefaultConfigOutput, Any, Any], Interceptor[UpdateDimensionInput, UpdateDimensionOutput, Any, Any], Interceptor[UpdateExperimentGroupInput, UpdateExperimentGroupOutput, Any, Any], Interceptor[UpdateFunctionInput, UpdateFunctionOutput, Any, Any], Interceptor[UpdateOrganisationInput, UpdateOrganisationOutput, Any, Any], Interceptor[UpdateOverrideInput, UpdateOverrideOutput, Any, Any], Interceptor[UpdateOverridesExperimentInput, UpdateOverridesExperimentOutput, Any, Any], Interceptor[UpdateSecretInput, UpdateSecretOutput, Any, Any], Interceptor[UpdateTypeTemplatesInput, UpdateTypeTemplatesOutput, Any, Any], Interceptor[UpdateVariableInput, UpdateVariableOutput, Any, Any], Interceptor[UpdateWebhookInput, UpdateWebhookOutput, Any, Any], Interceptor[UpdateWorkspaceInput, UpdateWorkspaceOutput, Any, Any], Interceptor[ValidateContextInput, ValidateContextOutput, Any, Any], Interceptor[WeightRecomputeInput, WeightRecomputeOutput, Any, Any]] +_ServiceInterceptor = Union[Interceptor[AddMembersToGroupInput, AddMembersToGroupOutput, Any, Any], Interceptor[ApplicableVariantsInput, ApplicableVariantsOutput, Any, Any], Interceptor[BulkOperationInput, BulkOperationOutput, Any, Any], Interceptor[ConcludeExperimentInput, ConcludeExperimentOutput, Any, Any], Interceptor[CreateContextInput, CreateContextOutput, Any, Any], Interceptor[CreateDefaultConfigInput, CreateDefaultConfigOutput, Any, Any], Interceptor[CreateDimensionInput, CreateDimensionOutput, Any, Any], Interceptor[CreateExperimentInput, CreateExperimentOutput, Any, Any], Interceptor[CreateExperimentGroupInput, CreateExperimentGroupOutput, Any, Any], Interceptor[CreateFunctionInput, CreateFunctionOutput, Any, Any], Interceptor[CreateOrganisationInput, CreateOrganisationOutput, Any, Any], Interceptor[CreateSecretInput, CreateSecretOutput, Any, Any], Interceptor[CreateTypeTemplatesInput, CreateTypeTemplatesOutput, Any, Any], Interceptor[CreateVariableInput, CreateVariableOutput, Any, Any], Interceptor[CreateWebhookInput, CreateWebhookOutput, Any, Any], Interceptor[CreateWorkspaceInput, CreateWorkspaceOutput, Any, Any], Interceptor[DeleteContextInput, DeleteContextOutput, Any, Any], Interceptor[DeleteDefaultConfigInput, DeleteDefaultConfigOutput, Any, Any], Interceptor[DeleteDimensionInput, DeleteDimensionOutput, Any, Any], Interceptor[DeleteExperimentGroupInput, DeleteExperimentGroupOutput, Any, Any], Interceptor[DeleteFunctionInput, DeleteFunctionOutput, Any, Any], Interceptor[DeleteSecretInput, DeleteSecretOutput, Any, Any], Interceptor[DeleteTypeTemplatesInput, DeleteTypeTemplatesOutput, Any, Any], Interceptor[DeleteVariableInput, DeleteVariableOutput, Any, Any], Interceptor[DeleteWebhookInput, DeleteWebhookOutput, Any, Any], Interceptor[DiscardExperimentInput, DiscardExperimentOutput, Any, Any], Interceptor[GetConfigInput, GetConfigOutput, Any, Any], Interceptor[GetConfigJsonInput, GetConfigJsonOutput, Any, Any], Interceptor[GetConfigTomlInput, GetConfigTomlOutput, Any, Any], Interceptor[GetContextInput, GetContextOutput, Any, Any], Interceptor[GetContextFromConditionInput, GetContextFromConditionOutput, Any, Any], Interceptor[GetDefaultConfigInput, GetDefaultConfigOutput, Any, Any], Interceptor[GetDimensionInput, GetDimensionOutput, Any, Any], Interceptor[GetExperimentInput, GetExperimentOutput, Any, Any], Interceptor[GetExperimentConfigInput, GetExperimentConfigOutput, Any, Any], Interceptor[GetExperimentGroupInput, GetExperimentGroupOutput, Any, Any], Interceptor[GetFunctionInput, GetFunctionOutput, Any, Any], Interceptor[GetOrganisationInput, GetOrganisationOutput, Any, Any], Interceptor[GetResolvedConfigInput, GetResolvedConfigOutput, Any, Any], Interceptor[GetResolvedConfigWithIdentifierInput, GetResolvedConfigWithIdentifierOutput, Any, Any], Interceptor[GetSecretInput, GetSecretOutput, Any, Any], Interceptor[GetTypeTemplateInput, GetTypeTemplateOutput, Any, Any], Interceptor[GetTypeTemplatesListInput, GetTypeTemplatesListOutput, Any, Any], Interceptor[GetVariableInput, GetVariableOutput, Any, Any], Interceptor[GetVersionInput, GetVersionOutput, Any, Any], Interceptor[GetWebhookInput, GetWebhookOutput, Any, Any], Interceptor[GetWebhookByEventInput, GetWebhookByEventOutput, Any, Any], Interceptor[GetWorkspaceInput, GetWorkspaceOutput, Any, Any], Interceptor[ListAuditLogsInput, ListAuditLogsOutput, Any, Any], Interceptor[ListContextsInput, ListContextsOutput, Any, Any], Interceptor[ListDefaultConfigsInput, ListDefaultConfigsOutput, Any, Any], Interceptor[ListDimensionsInput, ListDimensionsOutput, Any, Any], Interceptor[ListExperimentInput, ListExperimentOutput, Any, Any], Interceptor[ListExperimentGroupsInput, ListExperimentGroupsOutput, Any, Any], Interceptor[ListFunctionInput, ListFunctionOutput, Any, Any], Interceptor[ListOrganisationInput, ListOrganisationOutput, Any, Any], Interceptor[ListSecretsInput, ListSecretsOutput, Any, Any], Interceptor[ListVariablesInput, ListVariablesOutput, Any, Any], Interceptor[ListVersionsInput, ListVersionsOutput, Any, Any], Interceptor[ListWebhookInput, ListWebhookOutput, Any, Any], Interceptor[ListWorkspaceInput, ListWorkspaceOutput, Any, Any], Interceptor[MigrateWorkspaceSchemaInput, MigrateWorkspaceSchemaOutput, Any, Any], Interceptor[MoveContextInput, MoveContextOutput, Any, Any], Interceptor[PauseExperimentInput, PauseExperimentOutput, Any, Any], Interceptor[PublishInput, PublishOutput, Any, Any], Interceptor[RampExperimentInput, RampExperimentOutput, Any, Any], Interceptor[RemoveMembersFromGroupInput, RemoveMembersFromGroupOutput, Any, Any], Interceptor[ResumeExperimentInput, ResumeExperimentOutput, Any, Any], Interceptor[RotateMasterEncryptionKeyInput, RotateMasterEncryptionKeyOutput, Any, Any], Interceptor[RotateWorkspaceEncryptionKeyInput, RotateWorkspaceEncryptionKeyOutput, Any, Any], Interceptor[TestInput, TestOutput, Any, Any], Interceptor[UpdateDefaultConfigInput, UpdateDefaultConfigOutput, Any, Any], Interceptor[UpdateDimensionInput, UpdateDimensionOutput, Any, Any], Interceptor[UpdateExperimentGroupInput, UpdateExperimentGroupOutput, Any, Any], Interceptor[UpdateFunctionInput, UpdateFunctionOutput, Any, Any], Interceptor[UpdateOrganisationInput, UpdateOrganisationOutput, Any, Any], Interceptor[UpdateOverrideInput, UpdateOverrideOutput, Any, Any], Interceptor[UpdateOverridesExperimentInput, UpdateOverridesExperimentOutput, Any, Any], Interceptor[UpdateSecretInput, UpdateSecretOutput, Any, Any], Interceptor[UpdateTypeTemplatesInput, UpdateTypeTemplatesOutput, Any, Any], Interceptor[UpdateVariableInput, UpdateVariableOutput, Any, Any], Interceptor[UpdateWebhookInput, UpdateWebhookOutput, Any, Any], Interceptor[UpdateWorkspaceInput, UpdateWorkspaceOutput, Any, Any], Interceptor[ValidateContextInput, ValidateContextOutput, Any, Any], Interceptor[WeightRecomputeInput, WeightRecomputeOutput, Any, Any]] @dataclass(init=False) class Config: """Configuration for Superposition.""" diff --git a/clients/python/sdk/superposition_sdk/deserialize.py b/clients/python/sdk/superposition_sdk/deserialize.py index 3ca72e279..e3166812f 100644 --- a/clients/python/sdk/superposition_sdk/deserialize.py +++ b/clients/python/sdk/superposition_sdk/deserialize.py @@ -41,7 +41,6 @@ DeleteVariableOutput, DeleteWebhookOutput, DiscardExperimentOutput, - GetConfigFastOutput, GetConfigJsonOutput, GetConfigOutput, GetConfigTomlOutput, @@ -49,6 +48,7 @@ GetContextOutput, GetDefaultConfigOutput, GetDimensionOutput, + GetExperimentConfigOutput, GetExperimentGroupOutput, GetExperimentOutput, GetFunctionOutput, @@ -786,9 +786,6 @@ async def _deserialize_get_config(http_response: HTTPResponse, config: Config) - case "last-modified": kwargs["last_modified"] = ensure_utc(datetime.fromisoformat(expect_type(str, value))) - case "x-audit-id": - kwargs["audit_id"] = value - case _: pass @@ -804,46 +801,6 @@ async def _deserialize_error_get_config(http_response: HTTPResponse, config: Con case _: return UnknownApiError(f"{code}: {message}") -async def _deserialize_get_config_fast(http_response: HTTPResponse, config: Config) -> GetConfigFastOutput: - if http_response.status != 200 and http_response.status >= 300: - raise await _deserialize_error_get_config_fast(http_response, config) - - kwargs: dict[str, Any] = {} - - body = await http_response.consume_body_async() - if body: - codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS) - deserializer = codec.create_deserializer(body) - kwargs["config"] = deserializer.read_document(_SCHEMA_DOCUMENT) - - for fld in http_response.fields: - for key, value in fld.as_tuples(): - _key_lowercase = key.lower() - match _key_lowercase: - case "x-config-version": - kwargs["version"] = value - - case "last-modified": - kwargs["last_modified"] = ensure_utc(datetime.fromisoformat(expect_type(str, value))) - - case "x-audit-id": - kwargs["audit_id"] = value - - case _: - pass - - return GetConfigFastOutput(**kwargs) - -async def _deserialize_error_get_config_fast(http_response: HTTPResponse, config: Config) -> ApiError: - code, message, parsed_body = await parse_rest_json_error_info(http_response) - - match code.lower(): - case "internalservererror": - return await _deserialize_error_internal_server_error(http_response, config, parsed_body, message) - - case _: - return UnknownApiError(f"{code}: {message}") - async def _deserialize_get_config_json(http_response: HTTPResponse, config: Config) -> GetConfigJsonOutput: if http_response.status != 200 and http_response.status >= 300: raise await _deserialize_error_get_config_json(http_response, config) @@ -1048,6 +1005,41 @@ async def _deserialize_error_get_experiment(http_response: HTTPResponse, config: case _: return UnknownApiError(f"{code}: {message}") +async def _deserialize_get_experiment_config(http_response: HTTPResponse, config: Config) -> GetExperimentConfigOutput: + if http_response.status != 200 and http_response.status >= 300: + raise await _deserialize_error_get_experiment_config(http_response, config) + + kwargs: dict[str, Any] = {} + + body = await http_response.consume_body_async() + if body: + codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS) + deserializer = codec.create_deserializer(body) + body_kwargs = GetExperimentConfigOutput.deserialize_kwargs(deserializer) + kwargs.update(body_kwargs) + + for fld in http_response.fields: + for key, value in fld.as_tuples(): + _key_lowercase = key.lower() + match _key_lowercase: + case "last-modified": + kwargs["last_modified"] = ensure_utc(datetime.fromisoformat(expect_type(str, value))) + + case _: + pass + + return GetExperimentConfigOutput(**kwargs) + +async def _deserialize_error_get_experiment_config(http_response: HTTPResponse, config: Config) -> ApiError: + code, message, parsed_body = await parse_rest_json_error_info(http_response) + + match code.lower(): + case "internalservererror": + return await _deserialize_error_internal_server_error(http_response, config, parsed_body, message) + + case _: + return UnknownApiError(f"{code}: {message}") + async def _deserialize_get_experiment_group(http_response: HTTPResponse, config: Config) -> GetExperimentGroupOutput: if http_response.status != 200 and http_response.status >= 300: raise await _deserialize_error_get_experiment_group(http_response, config) @@ -1546,6 +1538,16 @@ async def _deserialize_list_experiment(http_response: HTTPResponse, config: Conf body_kwargs = ListExperimentOutput.deserialize_kwargs(deserializer) kwargs.update(body_kwargs) + for fld in http_response.fields: + for key, value in fld.as_tuples(): + _key_lowercase = key.lower() + match _key_lowercase: + case "last-modified": + kwargs["last_modified"] = ensure_utc(datetime.fromisoformat(expect_type(str, value))) + + case _: + pass + return ListExperimentOutput(**kwargs) async def _deserialize_error_list_experiment(http_response: HTTPResponse, config: Config) -> ApiError: @@ -1571,6 +1573,16 @@ async def _deserialize_list_experiment_groups(http_response: HTTPResponse, confi body_kwargs = ListExperimentGroupsOutput.deserialize_kwargs(deserializer) kwargs.update(body_kwargs) + for fld in http_response.fields: + for key, value in fld.as_tuples(): + _key_lowercase = key.lower() + match _key_lowercase: + case "last-modified": + kwargs["last_modified"] = ensure_utc(datetime.fromisoformat(expect_type(str, value))) + + case _: + pass + return ListExperimentGroupsOutput(**kwargs) async def _deserialize_error_list_experiment_groups(http_response: HTTPResponse, config: Config) -> ApiError: diff --git a/clients/python/sdk/superposition_sdk/models.py b/clients/python/sdk/superposition_sdk/models.py index d43c3ba50..abff3d688 100644 --- a/clients/python/sdk/superposition_sdk/models.py +++ b/clients/python/sdk/superposition_sdk/models.py @@ -114,9 +114,6 @@ FUNCTION_EXECUTION_REQUEST as _SCHEMA_FUNCTION_EXECUTION_REQUEST, FUNCTION_RESPONSE as _SCHEMA_FUNCTION_RESPONSE, GET_CONFIG as _SCHEMA_GET_CONFIG, - GET_CONFIG_FAST as _SCHEMA_GET_CONFIG_FAST, - GET_CONFIG_FAST_INPUT as _SCHEMA_GET_CONFIG_FAST_INPUT, - GET_CONFIG_FAST_OUTPUT as _SCHEMA_GET_CONFIG_FAST_OUTPUT, GET_CONFIG_INPUT as _SCHEMA_GET_CONFIG_INPUT, GET_CONFIG_JSON as _SCHEMA_GET_CONFIG_JSON, GET_CONFIG_JSON_INPUT as _SCHEMA_GET_CONFIG_JSON_INPUT, @@ -138,6 +135,9 @@ GET_DIMENSION_INPUT as _SCHEMA_GET_DIMENSION_INPUT, GET_DIMENSION_OUTPUT as _SCHEMA_GET_DIMENSION_OUTPUT, GET_EXPERIMENT as _SCHEMA_GET_EXPERIMENT, + GET_EXPERIMENT_CONFIG as _SCHEMA_GET_EXPERIMENT_CONFIG, + GET_EXPERIMENT_CONFIG_INPUT as _SCHEMA_GET_EXPERIMENT_CONFIG_INPUT, + GET_EXPERIMENT_CONFIG_OUTPUT as _SCHEMA_GET_EXPERIMENT_CONFIG_OUTPUT, GET_EXPERIMENT_GROUP as _SCHEMA_GET_EXPERIMENT_GROUP, GET_EXPERIMENT_GROUP_INPUT as _SCHEMA_GET_EXPERIMENT_GROUP_INPUT, GET_EXPERIMENT_GROUP_OUTPUT as _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT, @@ -2232,6 +2232,12 @@ def _read_value(k: str, d: ShapeDeserializer): class GetConfigInput: """ + :param if_modified_since: + While using this, 304 response is treated as error, which needs to be handled + separately by checking the response code of the http response. This is required + to make sure that clients can cache the response and avoid unnecessary calls + when there are no updates. + :param context: Map representing the context. Keys correspond to the names of the dimensions. @@ -2241,6 +2247,7 @@ class GetConfigInput: org_id: str | None = None prefix: list[str] | None = None version: str | None = None + if_modified_since: datetime | None = None context: dict[str, Document] | None = None def serialize(self, serializer: ShapeSerializer): @@ -2273,6 +2280,9 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: kwargs["version"] = de.read_string(_SCHEMA_GET_CONFIG_INPUT.members["version"]) case 4: + kwargs["if_modified_since"] = de.read_timestamp(_SCHEMA_GET_CONFIG_INPUT.members["if_modified_since"]) + + case 5: kwargs["context"] = _deserialize_context_map(de, _SCHEMA_GET_CONFIG_INPUT.members["context"]) case _: @@ -2659,8 +2669,6 @@ class GetConfigOutput: last_modified: datetime - audit_id: str | None = None - def serialize(self, serializer: ShapeSerializer): serializer.write_struct(_SCHEMA_GET_CONFIG_OUTPUT, self) @@ -2698,9 +2706,6 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: case 5: kwargs["last_modified"] = de.read_timestamp(_SCHEMA_GET_CONFIG_OUTPUT.members["last_modified"]) - case 6: - kwargs["audit_id"] = de.read_string(_SCHEMA_GET_CONFIG_OUTPUT.members["audit_id"]) - case _: logger.debug("Unexpected member schema: %s", schema) @@ -2723,101 +2728,20 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: ) @dataclass(kw_only=True) -class GetConfigFastInput: - - workspace_id: str | None = None - org_id: str | None = None - - def serialize(self, serializer: ShapeSerializer): - serializer.write_struct(_SCHEMA_GET_CONFIG_FAST_INPUT, self) - - def serialize_members(self, serializer: ShapeSerializer): - pass - - @classmethod - def deserialize(cls, deserializer: ShapeDeserializer) -> Self: - return cls(**cls.deserialize_kwargs(deserializer)) - - @classmethod - def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: - kwargs: dict[str, Any] = {} - - def _consumer(schema: Schema, de: ShapeDeserializer) -> None: - match schema.expect_member_index(): - case 0: - kwargs["workspace_id"] = de.read_string(_SCHEMA_GET_CONFIG_FAST_INPUT.members["workspace_id"]) - - case 1: - kwargs["org_id"] = de.read_string(_SCHEMA_GET_CONFIG_FAST_INPUT.members["org_id"]) - - case _: - logger.debug("Unexpected member schema: %s", schema) - - deserializer.read_struct(_SCHEMA_GET_CONFIG_FAST_INPUT, consumer=_consumer) - return kwargs - -@dataclass(kw_only=True) -class GetConfigFastOutput: - - config: Document | None = None - version: str | None = None - last_modified: datetime | None = None - audit_id: str | None = None - - def serialize(self, serializer: ShapeSerializer): - serializer.write_struct(_SCHEMA_GET_CONFIG_FAST_OUTPUT, self) - - def serialize_members(self, serializer: ShapeSerializer): - pass - - @classmethod - def deserialize(cls, deserializer: ShapeDeserializer) -> Self: - return cls(**cls.deserialize_kwargs(deserializer)) - - @classmethod - def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: - kwargs: dict[str, Any] = {} - - def _consumer(schema: Schema, de: ShapeDeserializer) -> None: - match schema.expect_member_index(): - case 0: - kwargs["config"] = de.read_document(_SCHEMA_GET_CONFIG_FAST_OUTPUT.members["config"]) - - case 1: - kwargs["version"] = de.read_string(_SCHEMA_GET_CONFIG_FAST_OUTPUT.members["version"]) - - case 2: - kwargs["last_modified"] = de.read_timestamp(_SCHEMA_GET_CONFIG_FAST_OUTPUT.members["last_modified"]) - - case 3: - kwargs["audit_id"] = de.read_string(_SCHEMA_GET_CONFIG_FAST_OUTPUT.members["audit_id"]) - - case _: - logger.debug("Unexpected member schema: %s", schema) - - deserializer.read_struct(_SCHEMA_GET_CONFIG_FAST_OUTPUT, consumer=_consumer) - return kwargs +class GetConfigJsonInput: + """ -GET_CONFIG_FAST = APIOperation( - input = GetConfigFastInput, - output = GetConfigFastOutput, - schema = _SCHEMA_GET_CONFIG_FAST, - input_schema = _SCHEMA_GET_CONFIG_FAST_INPUT, - output_schema = _SCHEMA_GET_CONFIG_FAST_OUTPUT, - error_registry = TypeRegistry({ - ShapeID("io.superposition#InternalServerError"): InternalServerError, - }), - effective_auth_schemes = [ - ShapeID("smithy.api#httpBasicAuth"), -ShapeID("smithy.api#httpBearerAuth") - ] -) + :param if_modified_since: + While using this, 304 response is treated as error, which needs to be handled + separately by checking the response code of the http response. This is required + to make sure that clients can cache the response and avoid unnecessary calls + when there are no updates. -@dataclass(kw_only=True) -class GetConfigJsonInput: + """ workspace_id: str | None = None org_id: str | None = None + if_modified_since: datetime | None = None def serialize(self, serializer: ShapeSerializer): serializer.write_struct(_SCHEMA_GET_CONFIG_JSON_INPUT, self) @@ -2841,6 +2765,9 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: case 1: kwargs["org_id"] = de.read_string(_SCHEMA_GET_CONFIG_JSON_INPUT.members["org_id"]) + case 2: + kwargs["if_modified_since"] = de.read_timestamp(_SCHEMA_GET_CONFIG_JSON_INPUT.members["if_modified_since"]) + case _: logger.debug("Unexpected member schema: %s", schema) @@ -2899,9 +2826,19 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: @dataclass(kw_only=True) class GetConfigTomlInput: + """ + + :param if_modified_since: + While using this, 304 response is treated as error, which needs to be handled + separately by checking the response code of the http response. This is required + to make sure that clients can cache the response and avoid unnecessary calls + when there are no updates. + + """ workspace_id: str | None = None org_id: str | None = None + if_modified_since: datetime | None = None def serialize(self, serializer: ShapeSerializer): serializer.write_struct(_SCHEMA_GET_CONFIG_TOML_INPUT, self) @@ -2925,6 +2862,9 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: case 1: kwargs["org_id"] = de.read_string(_SCHEMA_GET_CONFIG_TOML_INPUT.members["org_id"]) + case 2: + kwargs["if_modified_since"] = de.read_timestamp(_SCHEMA_GET_CONFIG_TOML_INPUT.members["if_modified_since"]) + case _: logger.debug("Unexpected member schema: %s", schema) @@ -9547,45 +9487,7 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: ) @dataclass(kw_only=True) -class GetExperimentGroupInput: - - workspace_id: str | None = None - org_id: str | None = None - id: str | None = None - - def serialize(self, serializer: ShapeSerializer): - serializer.write_struct(_SCHEMA_GET_EXPERIMENT_GROUP_INPUT, self) - - def serialize_members(self, serializer: ShapeSerializer): - pass - - @classmethod - def deserialize(cls, deserializer: ShapeDeserializer) -> Self: - return cls(**cls.deserialize_kwargs(deserializer)) - - @classmethod - def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: - kwargs: dict[str, Any] = {} - - def _consumer(schema: Schema, de: ShapeDeserializer) -> None: - match schema.expect_member_index(): - case 0: - kwargs["workspace_id"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_INPUT.members["workspace_id"]) - - case 1: - kwargs["org_id"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_INPUT.members["org_id"]) - - case 2: - kwargs["id"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_INPUT.members["id"]) - - case _: - logger.debug("Unexpected member schema: %s", schema) - - deserializer.read_struct(_SCHEMA_GET_EXPERIMENT_GROUP_INPUT, consumer=_consumer) - return kwargs - -@dataclass(kw_only=True) -class GetExperimentGroupOutput: +class ExperimentGroupResponse: """ Standard response structure for an experiment group. @@ -9624,23 +9526,23 @@ class GetExperimentGroupOutput: group_type: str def serialize(self, serializer: ShapeSerializer): - serializer.write_struct(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT, self) + serializer.write_struct(_SCHEMA_EXPERIMENT_GROUP_RESPONSE, self) def serialize_members(self, serializer: ShapeSerializer): - serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["id"], self.id) - serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["context_hash"], self.context_hash) - serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["name"], self.name) - serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["description"], self.description) - serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["change_reason"], self.change_reason) - _serialize_condition(serializer, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["context"], self.context) - serializer.write_integer(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["traffic_percentage"], self.traffic_percentage) - _serialize_string_list(serializer, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["member_experiment_ids"], self.member_experiment_ids) - serializer.write_timestamp(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["created_at"], self.created_at) - serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["created_by"], self.created_by) - serializer.write_timestamp(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["last_modified_at"], self.last_modified_at) - serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["last_modified_by"], self.last_modified_by) - _serialize_buckets(serializer, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["buckets"], self.buckets) - serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["group_type"], self.group_type) + serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["id"], self.id) + serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["context_hash"], self.context_hash) + serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["name"], self.name) + serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["description"], self.description) + serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["change_reason"], self.change_reason) + _serialize_condition(serializer, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["context"], self.context) + serializer.write_integer(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["traffic_percentage"], self.traffic_percentage) + _serialize_string_list(serializer, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["member_experiment_ids"], self.member_experiment_ids) + serializer.write_timestamp(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["created_at"], self.created_at) + serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["created_by"], self.created_by) + serializer.write_timestamp(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["last_modified_at"], self.last_modified_at) + serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["last_modified_by"], self.last_modified_by) + _serialize_buckets(serializer, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["buckets"], self.buckets) + serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["group_type"], self.group_type) @classmethod def deserialize(cls, deserializer: ShapeDeserializer) -> Self: @@ -9653,156 +9555,150 @@ def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: def _consumer(schema: Schema, de: ShapeDeserializer) -> None: match schema.expect_member_index(): case 0: - kwargs["id"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["id"]) + kwargs["id"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["id"]) case 1: - kwargs["context_hash"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["context_hash"]) + kwargs["context_hash"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["context_hash"]) case 2: - kwargs["name"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["name"]) + kwargs["name"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["name"]) case 3: - kwargs["description"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["description"]) + kwargs["description"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["description"]) case 4: - kwargs["change_reason"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["change_reason"]) + kwargs["change_reason"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["change_reason"]) case 5: - kwargs["context"] = _deserialize_condition(de, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["context"]) + kwargs["context"] = _deserialize_condition(de, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["context"]) case 6: - kwargs["traffic_percentage"] = de.read_integer(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["traffic_percentage"]) + kwargs["traffic_percentage"] = de.read_integer(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["traffic_percentage"]) case 7: - kwargs["member_experiment_ids"] = _deserialize_string_list(de, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["member_experiment_ids"]) + kwargs["member_experiment_ids"] = _deserialize_string_list(de, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["member_experiment_ids"]) case 8: - kwargs["created_at"] = de.read_timestamp(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["created_at"]) + kwargs["created_at"] = de.read_timestamp(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["created_at"]) case 9: - kwargs["created_by"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["created_by"]) + kwargs["created_by"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["created_by"]) case 10: - kwargs["last_modified_at"] = de.read_timestamp(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["last_modified_at"]) + kwargs["last_modified_at"] = de.read_timestamp(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["last_modified_at"]) case 11: - kwargs["last_modified_by"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["last_modified_by"]) + kwargs["last_modified_by"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["last_modified_by"]) case 12: - kwargs["buckets"] = _deserialize_buckets(de, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["buckets"]) + kwargs["buckets"] = _deserialize_buckets(de, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["buckets"]) case 13: - kwargs["group_type"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["group_type"]) + kwargs["group_type"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["group_type"]) case _: logger.debug("Unexpected member schema: %s", schema) - deserializer.read_struct(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT, consumer=_consumer) + deserializer.read_struct(_SCHEMA_EXPERIMENT_GROUP_RESPONSE, consumer=_consumer) return kwargs -GET_EXPERIMENT_GROUP = APIOperation( - input = GetExperimentGroupInput, - output = GetExperimentGroupOutput, - schema = _SCHEMA_GET_EXPERIMENT_GROUP, - input_schema = _SCHEMA_GET_EXPERIMENT_GROUP_INPUT, - output_schema = _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT, - error_registry = TypeRegistry({ - ShapeID("io.superposition#ResourceNotFound"): ResourceNotFound, -ShapeID("io.superposition#InternalServerError"): InternalServerError, - }), - effective_auth_schemes = [ - ShapeID("smithy.api#httpBasicAuth"), -ShapeID("smithy.api#httpBearerAuth") - ] -) - -def _serialize_group_type_list(serializer: ShapeSerializer, schema: Schema, value: list[str]) -> None: +def _serialize_experiment_group_list(serializer: ShapeSerializer, schema: Schema, value: list[ExperimentGroupResponse]) -> None: member_schema = schema.members["member"] with serializer.begin_list(schema, len(value)) as ls: for e in value: - ls.write_string(member_schema, e) + ls.write_struct(member_schema, e) -def _deserialize_group_type_list(deserializer: ShapeDeserializer, schema: Schema) -> list[str]: - result: list[str] = [] - member_schema = schema.members["member"] +def _deserialize_experiment_group_list(deserializer: ShapeDeserializer, schema: Schema) -> list[ExperimentGroupResponse]: + result: list[ExperimentGroupResponse] = [] def _read_value(d: ShapeDeserializer): if d.is_null(): d.read_null() else: - result.append(d.read_string(member_schema)) + result.append(ExperimentGroupResponse.deserialize(d)) deserializer.read_list(schema, _read_value) return result -class ExperimentGroupSortOn(StrEnum): - NAME = "name" +@dataclass(kw_only=True) +class ExperimentResponse: """ - Sort by name. - """ - CREATED_AT = "created_at" - """ - Sort by creation timestamp. + :param context: + **[Required]** - Represents conditional criteria used for context matching. Keys + define dimension names and values specify the criteria that must be met. """ - LAST_MODIFIED_AT = "last_modified_at" - """ - Sort by last modification timestamp. - """ + id: str -@dataclass(kw_only=True) -class ListExperimentGroupsInput: - """ + created_at: datetime - :param count: - Number of items to be returned in each page. + created_by: str - :param page: - Page number to retrieve, starting from 1. + last_modified: datetime - :param all: - If true, returns all requested items, ignoring pagination parameters page and - count. + name: str - :param name: - Filter by experiment group name (exact match or substring, depending on backend - implementation). + experiment_type: str - :param created_by: - Filter by the user who created the experiment group. + override_keys: list[str] - :param last_modified_by: - Filter by the user who last modified the experiment group. + status: str - :param sort_on: - Field to sort the results by. + traffic_percentage: int - :param sort_by: - Sort order (ascending or descending). + context: dict[str, Document] - :param group_type: - Filter by the type of group (USER_CREATED or SYSTEM_GENERATED). + variants: list[Variant] - """ + last_modified_by: str - count: int | None = None - page: int | None = None - all: bool | None = None - workspace_id: str | None = None - org_id: str | None = None - name: str | None = None - created_by: str | None = None - last_modified_by: str | None = None - sort_on: str | None = None - sort_by: str | None = None - group_type: list[str] | None = None + description: str + + change_reason: str + + chosen_variant: str | None = None + started_at: datetime | None = None + started_by: str | None = None + metrics_url: str | None = None + metrics: Document | None = None + experiment_group_id: str | None = None def serialize(self, serializer: ShapeSerializer): - serializer.write_struct(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT, self) + serializer.write_struct(_SCHEMA_EXPERIMENT_RESPONSE, self) def serialize_members(self, serializer: ShapeSerializer): - pass + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["id"], self.id) + serializer.write_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["created_at"], self.created_at) + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["created_by"], self.created_by) + serializer.write_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["last_modified"], self.last_modified) + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["name"], self.name) + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["experiment_type"], self.experiment_type) + _serialize_list_override_keys(serializer, _SCHEMA_EXPERIMENT_RESPONSE.members["override_keys"], self.override_keys) + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["status"], self.status) + serializer.write_integer(_SCHEMA_EXPERIMENT_RESPONSE.members["traffic_percentage"], self.traffic_percentage) + _serialize_condition(serializer, _SCHEMA_EXPERIMENT_RESPONSE.members["context"], self.context) + _serialize_list_variant(serializer, _SCHEMA_EXPERIMENT_RESPONSE.members["variants"], self.variants) + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["last_modified_by"], self.last_modified_by) + if self.chosen_variant is not None: + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["chosen_variant"], self.chosen_variant) + + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["description"], self.description) + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["change_reason"], self.change_reason) + if self.started_at is not None: + serializer.write_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["started_at"], self.started_at) + + if self.started_by is not None: + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["started_by"], self.started_by) + + if self.metrics_url is not None: + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["metrics_url"], self.metrics_url) + + if self.metrics is not None: + serializer.write_document(_SCHEMA_EXPERIMENT_RESPONSE.members["metrics"], self.metrics) + + if self.experiment_group_id is not None: + serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["experiment_group_id"], self.experiment_group_id) @classmethod def deserialize(cls, deserializer: ShapeDeserializer) -> Self: @@ -9815,46 +9711,256 @@ def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: def _consumer(schema: Schema, de: ShapeDeserializer) -> None: match schema.expect_member_index(): case 0: - kwargs["count"] = de.read_integer(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["count"]) + kwargs["id"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["id"]) case 1: - kwargs["page"] = de.read_integer(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["page"]) + kwargs["created_at"] = de.read_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["created_at"]) case 2: - kwargs["all"] = de.read_boolean(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["all"]) + kwargs["created_by"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["created_by"]) case 3: - kwargs["workspace_id"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["workspace_id"]) + kwargs["last_modified"] = de.read_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["last_modified"]) case 4: - kwargs["org_id"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["org_id"]) + kwargs["name"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["name"]) case 5: - kwargs["name"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["name"]) + kwargs["experiment_type"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["experiment_type"]) case 6: - kwargs["created_by"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["created_by"]) + kwargs["override_keys"] = _deserialize_list_override_keys(de, _SCHEMA_EXPERIMENT_RESPONSE.members["override_keys"]) case 7: - kwargs["last_modified_by"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["last_modified_by"]) + kwargs["status"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["status"]) case 8: - kwargs["sort_on"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["sort_on"]) + kwargs["traffic_percentage"] = de.read_integer(_SCHEMA_EXPERIMENT_RESPONSE.members["traffic_percentage"]) case 9: - kwargs["sort_by"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["sort_by"]) + kwargs["context"] = _deserialize_condition(de, _SCHEMA_EXPERIMENT_RESPONSE.members["context"]) case 10: - kwargs["group_type"] = _deserialize_group_type_list(de, _SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["group_type"]) + kwargs["variants"] = _deserialize_list_variant(de, _SCHEMA_EXPERIMENT_RESPONSE.members["variants"]) + + case 11: + kwargs["last_modified_by"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["last_modified_by"]) + + case 12: + kwargs["chosen_variant"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["chosen_variant"]) + + case 13: + kwargs["description"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["description"]) + + case 14: + kwargs["change_reason"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["change_reason"]) + + case 15: + kwargs["started_at"] = de.read_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["started_at"]) + + case 16: + kwargs["started_by"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["started_by"]) + + case 17: + kwargs["metrics_url"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["metrics_url"]) + + case 18: + kwargs["metrics"] = de.read_document(_SCHEMA_EXPERIMENT_RESPONSE.members["metrics"]) + + case 19: + kwargs["experiment_group_id"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["experiment_group_id"]) case _: logger.debug("Unexpected member schema: %s", schema) - deserializer.read_struct(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT, consumer=_consumer) + deserializer.read_struct(_SCHEMA_EXPERIMENT_RESPONSE, consumer=_consumer) + return kwargs + +def _serialize_experiment_list(serializer: ShapeSerializer, schema: Schema, value: list[ExperimentResponse]) -> None: + member_schema = schema.members["member"] + with serializer.begin_list(schema, len(value)) as ls: + for e in value: + ls.write_struct(member_schema, e) + +def _deserialize_experiment_list(deserializer: ShapeDeserializer, schema: Schema) -> list[ExperimentResponse]: + result: list[ExperimentResponse] = [] + def _read_value(d: ShapeDeserializer): + if d.is_null(): + d.read_null() + + else: + result.append(ExperimentResponse.deserialize(d)) + deserializer.read_list(schema, _read_value) + return result + +@dataclass(kw_only=True) +class GetExperimentConfigInput: + """ + + :param if_modified_since: + While using this, 304 response is treated as error, which needs to be handled + separately by checking the response code of the http response. This is required + to make sure that clients can cache the response and avoid unnecessary calls + when there are no updates. + + :param context: + Map representing the context. Keys correspond to the names of the dimensions. + + :param dimension_match_strategy: + Strategy to follow while filter items based on the context + + """ + + workspace_id: str | None = None + org_id: str | None = None + if_modified_since: datetime | None = None + prefix: list[str] | None = None + context: dict[str, Document] | None = None + dimension_match_strategy: str | None = None + + def serialize(self, serializer: ShapeSerializer): + serializer.write_struct(_SCHEMA_GET_EXPERIMENT_CONFIG_INPUT, self) + + def serialize_members(self, serializer: ShapeSerializer): + if self.context is not None: + _serialize_context_map(serializer, _SCHEMA_GET_EXPERIMENT_CONFIG_INPUT.members["context"], self.context) + + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> Self: + return cls(**cls.deserialize_kwargs(deserializer)) + + @classmethod + def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + match schema.expect_member_index(): + case 0: + kwargs["workspace_id"] = de.read_string(_SCHEMA_GET_EXPERIMENT_CONFIG_INPUT.members["workspace_id"]) + + case 1: + kwargs["org_id"] = de.read_string(_SCHEMA_GET_EXPERIMENT_CONFIG_INPUT.members["org_id"]) + + case 2: + kwargs["if_modified_since"] = de.read_timestamp(_SCHEMA_GET_EXPERIMENT_CONFIG_INPUT.members["if_modified_since"]) + + case 3: + kwargs["prefix"] = _deserialize_string_list(de, _SCHEMA_GET_EXPERIMENT_CONFIG_INPUT.members["prefix"]) + + case 4: + kwargs["context"] = _deserialize_context_map(de, _SCHEMA_GET_EXPERIMENT_CONFIG_INPUT.members["context"]) + + case 5: + kwargs["dimension_match_strategy"] = de.read_string(_SCHEMA_GET_EXPERIMENT_CONFIG_INPUT.members["dimension_match_strategy"]) + + case _: + logger.debug("Unexpected member schema: %s", schema) + + deserializer.read_struct(_SCHEMA_GET_EXPERIMENT_CONFIG_INPUT, consumer=_consumer) return kwargs @dataclass(kw_only=True) -class ExperimentGroupResponse: +class GetExperimentConfigOutput: + """ + + :param experiment_groups: + **[Required]** - A list of experiment group responses. + + """ + + last_modified: datetime + + experiments: list[ExperimentResponse] + + experiment_groups: list[ExperimentGroupResponse] + + def serialize(self, serializer: ShapeSerializer): + serializer.write_struct(_SCHEMA_GET_EXPERIMENT_CONFIG_OUTPUT, self) + + def serialize_members(self, serializer: ShapeSerializer): + _serialize_experiment_list(serializer, _SCHEMA_GET_EXPERIMENT_CONFIG_OUTPUT.members["experiments"], self.experiments) + _serialize_experiment_group_list(serializer, _SCHEMA_GET_EXPERIMENT_CONFIG_OUTPUT.members["experiment_groups"], self.experiment_groups) + + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> Self: + return cls(**cls.deserialize_kwargs(deserializer)) + + @classmethod + def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + match schema.expect_member_index(): + case 0: + kwargs["last_modified"] = de.read_timestamp(_SCHEMA_GET_EXPERIMENT_CONFIG_OUTPUT.members["last_modified"]) + + case 1: + kwargs["experiments"] = _deserialize_experiment_list(de, _SCHEMA_GET_EXPERIMENT_CONFIG_OUTPUT.members["experiments"]) + + case 2: + kwargs["experiment_groups"] = _deserialize_experiment_group_list(de, _SCHEMA_GET_EXPERIMENT_CONFIG_OUTPUT.members["experiment_groups"]) + + case _: + logger.debug("Unexpected member schema: %s", schema) + + deserializer.read_struct(_SCHEMA_GET_EXPERIMENT_CONFIG_OUTPUT, consumer=_consumer) + return kwargs + +GET_EXPERIMENT_CONFIG = APIOperation( + input = GetExperimentConfigInput, + output = GetExperimentConfigOutput, + schema = _SCHEMA_GET_EXPERIMENT_CONFIG, + input_schema = _SCHEMA_GET_EXPERIMENT_CONFIG_INPUT, + output_schema = _SCHEMA_GET_EXPERIMENT_CONFIG_OUTPUT, + error_registry = TypeRegistry({ + ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ + ShapeID("smithy.api#httpBasicAuth"), +ShapeID("smithy.api#httpBearerAuth") + ] +) + +@dataclass(kw_only=True) +class GetExperimentGroupInput: + + workspace_id: str | None = None + org_id: str | None = None + id: str | None = None + + def serialize(self, serializer: ShapeSerializer): + serializer.write_struct(_SCHEMA_GET_EXPERIMENT_GROUP_INPUT, self) + + def serialize_members(self, serializer: ShapeSerializer): + pass + + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> Self: + return cls(**cls.deserialize_kwargs(deserializer)) + + @classmethod + def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + match schema.expect_member_index(): + case 0: + kwargs["workspace_id"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_INPUT.members["workspace_id"]) + + case 1: + kwargs["org_id"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_INPUT.members["org_id"]) + + case 2: + kwargs["id"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_INPUT.members["id"]) + + case _: + logger.debug("Unexpected member schema: %s", schema) + + deserializer.read_struct(_SCHEMA_GET_EXPERIMENT_GROUP_INPUT, consumer=_consumer) + return kwargs + +@dataclass(kw_only=True) +class GetExperimentGroupOutput: """ Standard response structure for an experiment group. @@ -9893,23 +9999,23 @@ class ExperimentGroupResponse: group_type: str def serialize(self, serializer: ShapeSerializer): - serializer.write_struct(_SCHEMA_EXPERIMENT_GROUP_RESPONSE, self) + serializer.write_struct(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT, self) def serialize_members(self, serializer: ShapeSerializer): - serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["id"], self.id) - serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["context_hash"], self.context_hash) - serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["name"], self.name) - serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["description"], self.description) - serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["change_reason"], self.change_reason) - _serialize_condition(serializer, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["context"], self.context) - serializer.write_integer(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["traffic_percentage"], self.traffic_percentage) - _serialize_string_list(serializer, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["member_experiment_ids"], self.member_experiment_ids) - serializer.write_timestamp(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["created_at"], self.created_at) - serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["created_by"], self.created_by) - serializer.write_timestamp(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["last_modified_at"], self.last_modified_at) - serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["last_modified_by"], self.last_modified_by) - _serialize_buckets(serializer, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["buckets"], self.buckets) - serializer.write_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["group_type"], self.group_type) + serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["id"], self.id) + serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["context_hash"], self.context_hash) + serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["name"], self.name) + serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["description"], self.description) + serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["change_reason"], self.change_reason) + _serialize_condition(serializer, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["context"], self.context) + serializer.write_integer(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["traffic_percentage"], self.traffic_percentage) + _serialize_string_list(serializer, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["member_experiment_ids"], self.member_experiment_ids) + serializer.write_timestamp(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["created_at"], self.created_at) + serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["created_by"], self.created_by) + serializer.write_timestamp(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["last_modified_at"], self.last_modified_at) + serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["last_modified_by"], self.last_modified_by) + _serialize_buckets(serializer, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["buckets"], self.buckets) + serializer.write_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["group_type"], self.group_type) @classmethod def deserialize(cls, deserializer: ShapeDeserializer) -> Self: @@ -9922,70 +10028,231 @@ def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: def _consumer(schema: Schema, de: ShapeDeserializer) -> None: match schema.expect_member_index(): case 0: - kwargs["id"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["id"]) + kwargs["id"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["id"]) case 1: - kwargs["context_hash"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["context_hash"]) + kwargs["context_hash"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["context_hash"]) + + case 2: + kwargs["name"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["name"]) + + case 3: + kwargs["description"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["description"]) + + case 4: + kwargs["change_reason"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["change_reason"]) + + case 5: + kwargs["context"] = _deserialize_condition(de, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["context"]) + + case 6: + kwargs["traffic_percentage"] = de.read_integer(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["traffic_percentage"]) + + case 7: + kwargs["member_experiment_ids"] = _deserialize_string_list(de, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["member_experiment_ids"]) + + case 8: + kwargs["created_at"] = de.read_timestamp(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["created_at"]) + + case 9: + kwargs["created_by"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["created_by"]) + + case 10: + kwargs["last_modified_at"] = de.read_timestamp(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["last_modified_at"]) + + case 11: + kwargs["last_modified_by"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["last_modified_by"]) + + case 12: + kwargs["buckets"] = _deserialize_buckets(de, _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["buckets"]) + + case 13: + kwargs["group_type"] = de.read_string(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT.members["group_type"]) + + case _: + logger.debug("Unexpected member schema: %s", schema) + + deserializer.read_struct(_SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT, consumer=_consumer) + return kwargs + +GET_EXPERIMENT_GROUP = APIOperation( + input = GetExperimentGroupInput, + output = GetExperimentGroupOutput, + schema = _SCHEMA_GET_EXPERIMENT_GROUP, + input_schema = _SCHEMA_GET_EXPERIMENT_GROUP_INPUT, + output_schema = _SCHEMA_GET_EXPERIMENT_GROUP_OUTPUT, + error_registry = TypeRegistry({ + ShapeID("io.superposition#ResourceNotFound"): ResourceNotFound, +ShapeID("io.superposition#InternalServerError"): InternalServerError, + }), + effective_auth_schemes = [ + ShapeID("smithy.api#httpBasicAuth"), +ShapeID("smithy.api#httpBearerAuth") + ] +) + +def _serialize_group_type_list(serializer: ShapeSerializer, schema: Schema, value: list[str]) -> None: + member_schema = schema.members["member"] + with serializer.begin_list(schema, len(value)) as ls: + for e in value: + ls.write_string(member_schema, e) + +def _deserialize_group_type_list(deserializer: ShapeDeserializer, schema: Schema) -> list[str]: + result: list[str] = [] + member_schema = schema.members["member"] + def _read_value(d: ShapeDeserializer): + if d.is_null(): + d.read_null() + + else: + result.append(d.read_string(member_schema)) + deserializer.read_list(schema, _read_value) + return result + +class ExperimentGroupSortOn(StrEnum): + NAME = "name" + """ + Sort by name. + + """ + CREATED_AT = "created_at" + """ + Sort by creation timestamp. + + """ + LAST_MODIFIED_AT = "last_modified_at" + """ + Sort by last modification timestamp. + + """ + +@dataclass(kw_only=True) +class ListExperimentGroupsInput: + """ + + :param count: + Number of items to be returned in each page. + + :param page: + Page number to retrieve, starting from 1. + + :param all: + If true, returns all requested items, ignoring pagination parameters page and + count. + + :param if_modified_since: + While using this, 304 response is treated as error, which needs to be handled + separately by checking the response code of the http response. This is required + to make sure that clients can cache the response and avoid unnecessary calls + when there are no updates. + + :param name: + Filter by experiment group name (exact match or substring, depending on backend + implementation). + + :param created_by: + Filter by the user who created the experiment group. + + :param last_modified_by: + Filter by the user who last modified the experiment group. + + :param sort_on: + Field to sort the results by. + + :param sort_by: + Sort order (ascending or descending). + + :param group_type: + Filter by the type of group (USER_CREATED or SYSTEM_GENERATED). + + :param dimension_match_strategy: + Strategy to follow while filter items based on the context + + :param context: + Map representing the context. Keys correspond to the names of the dimensions. + + """ + + count: int | None = None + page: int | None = None + all: bool | None = None + workspace_id: str | None = None + org_id: str | None = None + if_modified_since: datetime | None = None + name: str | None = None + created_by: str | None = None + last_modified_by: str | None = None + sort_on: str | None = None + sort_by: str | None = None + group_type: list[str] | None = None + dimension_match_strategy: str | None = None + context: dict[str, Document] | None = None + + def serialize(self, serializer: ShapeSerializer): + serializer.write_struct(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT, self) + + def serialize_members(self, serializer: ShapeSerializer): + if self.context is not None: + _serialize_context_map(serializer, _SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["context"], self.context) + + @classmethod + def deserialize(cls, deserializer: ShapeDeserializer) -> Self: + return cls(**cls.deserialize_kwargs(deserializer)) + + @classmethod + def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: + kwargs: dict[str, Any] = {} + + def _consumer(schema: Schema, de: ShapeDeserializer) -> None: + match schema.expect_member_index(): + case 0: + kwargs["count"] = de.read_integer(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["count"]) + + case 1: + kwargs["page"] = de.read_integer(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["page"]) case 2: - kwargs["name"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["name"]) + kwargs["all"] = de.read_boolean(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["all"]) case 3: - kwargs["description"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["description"]) + kwargs["workspace_id"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["workspace_id"]) case 4: - kwargs["change_reason"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["change_reason"]) + kwargs["org_id"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["org_id"]) case 5: - kwargs["context"] = _deserialize_condition(de, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["context"]) + kwargs["if_modified_since"] = de.read_timestamp(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["if_modified_since"]) case 6: - kwargs["traffic_percentage"] = de.read_integer(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["traffic_percentage"]) + kwargs["name"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["name"]) case 7: - kwargs["member_experiment_ids"] = _deserialize_string_list(de, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["member_experiment_ids"]) + kwargs["created_by"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["created_by"]) case 8: - kwargs["created_at"] = de.read_timestamp(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["created_at"]) + kwargs["last_modified_by"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["last_modified_by"]) case 9: - kwargs["created_by"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["created_by"]) + kwargs["sort_on"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["sort_on"]) case 10: - kwargs["last_modified_at"] = de.read_timestamp(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["last_modified_at"]) + kwargs["sort_by"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["sort_by"]) case 11: - kwargs["last_modified_by"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["last_modified_by"]) + kwargs["group_type"] = _deserialize_group_type_list(de, _SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["group_type"]) case 12: - kwargs["buckets"] = _deserialize_buckets(de, _SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["buckets"]) + kwargs["dimension_match_strategy"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["dimension_match_strategy"]) case 13: - kwargs["group_type"] = de.read_string(_SCHEMA_EXPERIMENT_GROUP_RESPONSE.members["group_type"]) + kwargs["context"] = _deserialize_context_map(de, _SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT.members["context"]) case _: logger.debug("Unexpected member schema: %s", schema) - deserializer.read_struct(_SCHEMA_EXPERIMENT_GROUP_RESPONSE, consumer=_consumer) + deserializer.read_struct(_SCHEMA_LIST_EXPERIMENT_GROUPS_INPUT, consumer=_consumer) return kwargs -def _serialize_experiment_group_list(serializer: ShapeSerializer, schema: Schema, value: list[ExperimentGroupResponse]) -> None: - member_schema = schema.members["member"] - with serializer.begin_list(schema, len(value)) as ls: - for e in value: - ls.write_struct(member_schema, e) - -def _deserialize_experiment_group_list(deserializer: ShapeDeserializer, schema: Schema) -> list[ExperimentGroupResponse]: - result: list[ExperimentGroupResponse] = [] - def _read_value(d: ShapeDeserializer): - if d.is_null(): - d.read_null() - - else: - result.append(ExperimentGroupResponse.deserialize(d)) - deserializer.read_list(schema, _read_value) - return result - @dataclass(kw_only=True) class ListExperimentGroupsOutput: """ @@ -10001,6 +10268,8 @@ class ListExperimentGroupsOutput: data: list[ExperimentGroupResponse] + last_modified: datetime + def serialize(self, serializer: ShapeSerializer): serializer.write_struct(_SCHEMA_LIST_EXPERIMENT_GROUPS_OUTPUT, self) @@ -10028,6 +10297,9 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: case 2: kwargs["data"] = _deserialize_experiment_group_list(de, _SCHEMA_LIST_EXPERIMENT_GROUPS_OUTPUT.members["data"]) + case 3: + kwargs["last_modified"] = de.read_timestamp(_SCHEMA_LIST_EXPERIMENT_GROUPS_OUTPUT.members["last_modified"]) + case _: logger.debug("Unexpected member schema: %s", schema) @@ -10443,180 +10715,6 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: ] ) -@dataclass(kw_only=True) -class ExperimentResponse: - """ - - :param context: - **[Required]** - Represents conditional criteria used for context matching. Keys - define dimension names and values specify the criteria that must be met. - - """ - - id: str - - created_at: datetime - - created_by: str - - last_modified: datetime - - name: str - - experiment_type: str - - override_keys: list[str] - - status: str - - traffic_percentage: int - - context: dict[str, Document] - - variants: list[Variant] - - last_modified_by: str - - description: str - - change_reason: str - - chosen_variant: str | None = None - started_at: datetime | None = None - started_by: str | None = None - metrics_url: str | None = None - metrics: Document | None = None - experiment_group_id: str | None = None - - def serialize(self, serializer: ShapeSerializer): - serializer.write_struct(_SCHEMA_EXPERIMENT_RESPONSE, self) - - def serialize_members(self, serializer: ShapeSerializer): - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["id"], self.id) - serializer.write_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["created_at"], self.created_at) - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["created_by"], self.created_by) - serializer.write_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["last_modified"], self.last_modified) - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["name"], self.name) - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["experiment_type"], self.experiment_type) - _serialize_list_override_keys(serializer, _SCHEMA_EXPERIMENT_RESPONSE.members["override_keys"], self.override_keys) - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["status"], self.status) - serializer.write_integer(_SCHEMA_EXPERIMENT_RESPONSE.members["traffic_percentage"], self.traffic_percentage) - _serialize_condition(serializer, _SCHEMA_EXPERIMENT_RESPONSE.members["context"], self.context) - _serialize_list_variant(serializer, _SCHEMA_EXPERIMENT_RESPONSE.members["variants"], self.variants) - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["last_modified_by"], self.last_modified_by) - if self.chosen_variant is not None: - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["chosen_variant"], self.chosen_variant) - - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["description"], self.description) - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["change_reason"], self.change_reason) - if self.started_at is not None: - serializer.write_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["started_at"], self.started_at) - - if self.started_by is not None: - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["started_by"], self.started_by) - - if self.metrics_url is not None: - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["metrics_url"], self.metrics_url) - - if self.metrics is not None: - serializer.write_document(_SCHEMA_EXPERIMENT_RESPONSE.members["metrics"], self.metrics) - - if self.experiment_group_id is not None: - serializer.write_string(_SCHEMA_EXPERIMENT_RESPONSE.members["experiment_group_id"], self.experiment_group_id) - - @classmethod - def deserialize(cls, deserializer: ShapeDeserializer) -> Self: - return cls(**cls.deserialize_kwargs(deserializer)) - - @classmethod - def deserialize_kwargs(cls, deserializer: ShapeDeserializer) -> dict[str, Any]: - kwargs: dict[str, Any] = {} - - def _consumer(schema: Schema, de: ShapeDeserializer) -> None: - match schema.expect_member_index(): - case 0: - kwargs["id"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["id"]) - - case 1: - kwargs["created_at"] = de.read_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["created_at"]) - - case 2: - kwargs["created_by"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["created_by"]) - - case 3: - kwargs["last_modified"] = de.read_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["last_modified"]) - - case 4: - kwargs["name"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["name"]) - - case 5: - kwargs["experiment_type"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["experiment_type"]) - - case 6: - kwargs["override_keys"] = _deserialize_list_override_keys(de, _SCHEMA_EXPERIMENT_RESPONSE.members["override_keys"]) - - case 7: - kwargs["status"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["status"]) - - case 8: - kwargs["traffic_percentage"] = de.read_integer(_SCHEMA_EXPERIMENT_RESPONSE.members["traffic_percentage"]) - - case 9: - kwargs["context"] = _deserialize_condition(de, _SCHEMA_EXPERIMENT_RESPONSE.members["context"]) - - case 10: - kwargs["variants"] = _deserialize_list_variant(de, _SCHEMA_EXPERIMENT_RESPONSE.members["variants"]) - - case 11: - kwargs["last_modified_by"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["last_modified_by"]) - - case 12: - kwargs["chosen_variant"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["chosen_variant"]) - - case 13: - kwargs["description"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["description"]) - - case 14: - kwargs["change_reason"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["change_reason"]) - - case 15: - kwargs["started_at"] = de.read_timestamp(_SCHEMA_EXPERIMENT_RESPONSE.members["started_at"]) - - case 16: - kwargs["started_by"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["started_by"]) - - case 17: - kwargs["metrics_url"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["metrics_url"]) - - case 18: - kwargs["metrics"] = de.read_document(_SCHEMA_EXPERIMENT_RESPONSE.members["metrics"]) - - case 19: - kwargs["experiment_group_id"] = de.read_string(_SCHEMA_EXPERIMENT_RESPONSE.members["experiment_group_id"]) - - case _: - logger.debug("Unexpected member schema: %s", schema) - - deserializer.read_struct(_SCHEMA_EXPERIMENT_RESPONSE, consumer=_consumer) - return kwargs - -def _serialize_experiment_list(serializer: ShapeSerializer, schema: Schema, value: list[ExperimentResponse]) -> None: - member_schema = schema.members["member"] - with serializer.begin_list(schema, len(value)) as ls: - for e in value: - ls.write_struct(member_schema, e) - -def _deserialize_experiment_list(deserializer: ShapeDeserializer, schema: Schema) -> list[ExperimentResponse]: - result: list[ExperimentResponse] = [] - def _read_value(d: ShapeDeserializer): - if d.is_null(): - d.read_null() - - else: - result.append(ExperimentResponse.deserialize(d)) - deserializer.read_list(schema, _read_value) - return result - @dataclass(kw_only=True) class GetExperimentInput: @@ -10864,12 +10962,21 @@ class ListExperimentInput: If true, returns all requested items, ignoring pagination parameters page and count. + :param if_modified_since: + While using this, 304 response is treated as error, which needs to be handled + separately by checking the response code of the http response. This is required + to make sure that clients can cache the response and avoid unnecessary calls + when there are no updates. + :param sort_by: Sort order enumeration for list operations. :param dimension_match_strategy: Strategy to follow while filter items based on the context + :param context: + Map representing the context. Keys correspond to the names of the dimensions. + """ count: int | None = None @@ -10877,6 +10984,7 @@ class ListExperimentInput: all: bool | None = None workspace_id: str | None = None org_id: str | None = None + if_modified_since: datetime | None = None status: list[str] | None = None from_date: datetime | None = None to_date: datetime | None = None @@ -10888,12 +10996,15 @@ class ListExperimentInput: sort_by: str | None = None global_experiments_only: bool | None = None dimension_match_strategy: str | None = None + prefix: list[str] | None = None + context: dict[str, Document] | None = None def serialize(self, serializer: ShapeSerializer): serializer.write_struct(_SCHEMA_LIST_EXPERIMENT_INPUT, self) def serialize_members(self, serializer: ShapeSerializer): - pass + if self.context is not None: + _serialize_context_map(serializer, _SCHEMA_LIST_EXPERIMENT_INPUT.members["context"], self.context) @classmethod def deserialize(cls, deserializer: ShapeDeserializer) -> Self: @@ -10921,38 +11032,47 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: kwargs["org_id"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_INPUT.members["org_id"]) case 5: - kwargs["status"] = _deserialize_experiment_status_type_list(de, _SCHEMA_LIST_EXPERIMENT_INPUT.members["status"]) + kwargs["if_modified_since"] = de.read_timestamp(_SCHEMA_LIST_EXPERIMENT_INPUT.members["if_modified_since"]) case 6: - kwargs["from_date"] = de.read_timestamp(_SCHEMA_LIST_EXPERIMENT_INPUT.members["from_date"]) + kwargs["status"] = _deserialize_experiment_status_type_list(de, _SCHEMA_LIST_EXPERIMENT_INPUT.members["status"]) case 7: - kwargs["to_date"] = de.read_timestamp(_SCHEMA_LIST_EXPERIMENT_INPUT.members["to_date"]) + kwargs["from_date"] = de.read_timestamp(_SCHEMA_LIST_EXPERIMENT_INPUT.members["from_date"]) case 8: - kwargs["experiment_name"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_INPUT.members["experiment_name"]) + kwargs["to_date"] = de.read_timestamp(_SCHEMA_LIST_EXPERIMENT_INPUT.members["to_date"]) case 9: - kwargs["experiment_ids"] = _deserialize_string_list(de, _SCHEMA_LIST_EXPERIMENT_INPUT.members["experiment_ids"]) + kwargs["experiment_name"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_INPUT.members["experiment_name"]) case 10: - kwargs["experiment_group_ids"] = _deserialize_string_list(de, _SCHEMA_LIST_EXPERIMENT_INPUT.members["experiment_group_ids"]) + kwargs["experiment_ids"] = _deserialize_string_list(de, _SCHEMA_LIST_EXPERIMENT_INPUT.members["experiment_ids"]) case 11: - kwargs["created_by"] = _deserialize_string_list(de, _SCHEMA_LIST_EXPERIMENT_INPUT.members["created_by"]) + kwargs["experiment_group_ids"] = _deserialize_string_list(de, _SCHEMA_LIST_EXPERIMENT_INPUT.members["experiment_group_ids"]) case 12: - kwargs["sort_on"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_INPUT.members["sort_on"]) + kwargs["created_by"] = _deserialize_string_list(de, _SCHEMA_LIST_EXPERIMENT_INPUT.members["created_by"]) case 13: - kwargs["sort_by"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_INPUT.members["sort_by"]) + kwargs["sort_on"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_INPUT.members["sort_on"]) case 14: - kwargs["global_experiments_only"] = de.read_boolean(_SCHEMA_LIST_EXPERIMENT_INPUT.members["global_experiments_only"]) + kwargs["sort_by"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_INPUT.members["sort_by"]) case 15: + kwargs["global_experiments_only"] = de.read_boolean(_SCHEMA_LIST_EXPERIMENT_INPUT.members["global_experiments_only"]) + + case 16: kwargs["dimension_match_strategy"] = de.read_string(_SCHEMA_LIST_EXPERIMENT_INPUT.members["dimension_match_strategy"]) + case 17: + kwargs["prefix"] = _deserialize_string_list(de, _SCHEMA_LIST_EXPERIMENT_INPUT.members["prefix"]) + + case 18: + kwargs["context"] = _deserialize_context_map(de, _SCHEMA_LIST_EXPERIMENT_INPUT.members["context"]) + case _: logger.debug("Unexpected member schema: %s", schema) @@ -10968,6 +11088,8 @@ class ListExperimentOutput: data: list[ExperimentResponse] + last_modified: datetime + def serialize(self, serializer: ShapeSerializer): serializer.write_struct(_SCHEMA_LIST_EXPERIMENT_OUTPUT, self) @@ -10995,6 +11117,9 @@ def _consumer(schema: Schema, de: ShapeDeserializer) -> None: case 2: kwargs["data"] = _deserialize_experiment_list(de, _SCHEMA_LIST_EXPERIMENT_OUTPUT.members["data"]) + case 3: + kwargs["last_modified"] = de.read_timestamp(_SCHEMA_LIST_EXPERIMENT_OUTPUT.members["last_modified"]) + case _: logger.debug("Unexpected member schema: %s", schema) diff --git a/clients/python/sdk/superposition_sdk/serialize.py b/clients/python/sdk/superposition_sdk/serialize.py index 3bb4cae9f..d5a515a25 100644 --- a/clients/python/sdk/superposition_sdk/serialize.py +++ b/clients/python/sdk/superposition_sdk/serialize.py @@ -41,7 +41,6 @@ DeleteVariableInput, DeleteWebhookInput, DiscardExperimentInput, - GetConfigFastInput, GetConfigInput, GetConfigJsonInput, GetConfigTomlInput, @@ -49,6 +48,7 @@ GetContextInput, GetDefaultConfigInput, GetDimensionInput, + GetExperimentConfigInput, GetExperimentGroupInput, GetExperimentInput, GetFunctionInput, @@ -1055,6 +1055,8 @@ async def _serialize_get_config(input: GetConfigInput, config: Config) -> HTTPRe headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) if input.org_id: headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) + if input.if_modified_since is not None: + headers.extend(Fields([Field(name="if-modified-since", values=[serialize_rfc3339(ensure_utc(input.if_modified_since))])])) return _HTTPRequest( destination=_URI( host="", @@ -1067,33 +1069,6 @@ async def _serialize_get_config(input: GetConfigInput, config: Config) -> HTTPRe body=body, ) -async def _serialize_get_config_fast(input: GetConfigFastInput, config: Config) -> HTTPRequest: - path = "/config/fast" - query: str = f'' - - body: AsyncIterable[bytes] = AsyncBytesReader(b'') - headers = Fields( - [ - - ] - ) - - if input.workspace_id: - headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) - if input.org_id: - headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) - return _HTTPRequest( - destination=_URI( - host="", - path=path, - scheme="https", - query=query, - ), - method="GET", - fields=headers, - body=body, - ) - async def _serialize_get_config_json(input: GetConfigJsonInput, config: Config) -> HTTPRequest: path = "/config/json" query: str = f'' @@ -1109,6 +1084,8 @@ async def _serialize_get_config_json(input: GetConfigJsonInput, config: Config) headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) if input.org_id: headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) + if input.if_modified_since is not None: + headers.extend(Fields([Field(name="if-modified-since", values=[serialize_rfc3339(ensure_utc(input.if_modified_since))])])) return _HTTPRequest( destination=_URI( host="", @@ -1116,7 +1093,7 @@ async def _serialize_get_config_json(input: GetConfigJsonInput, config: Config) scheme="https", query=query, ), - method="GET", + method="POST", fields=headers, body=body, ) @@ -1136,6 +1113,8 @@ async def _serialize_get_config_toml(input: GetConfigTomlInput, config: Config) headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) if input.org_id: headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) + if input.if_modified_since is not None: + headers.extend(Fields([Field(name="if-modified-since", values=[serialize_rfc3339(ensure_utc(input.if_modified_since))])])) return _HTTPRequest( destination=_URI( host="", @@ -1143,7 +1122,7 @@ async def _serialize_get_config_toml(input: GetConfigTomlInput, config: Config) scheme="https", query=query, ), - method="GET", + method="POST", fields=headers, body=body, ) @@ -1312,6 +1291,52 @@ async def _serialize_get_experiment(input: GetExperimentInput, config: Config) - body=body, ) +async def _serialize_get_experiment_config(input: GetExperimentConfigInput, config: Config) -> HTTPRequest: + path = "/experiment-config" + query: str = f'' + + query_params: list[tuple[str, str | None]] = [] + if input.prefix is not None: + query_params.extend(("prefix", e) for e in input.prefix) + if input.dimension_match_strategy is not None: + query_params.append(("dimension_match_strategy", input.dimension_match_strategy)) + + query = join_query_params(params=query_params, prefix=query) + + body: AsyncIterable[bytes] = AsyncBytesReader(b'') + codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS) + content = codec.serialize(input) + if not content: + content = b"{}" + content_length = len(content) + body = SeekableAsyncBytesReader(content) + + headers = Fields( + [ + Field(name="Content-Type", values=["application/json"]), + Field(name="Content-Length", values=[str(content_length)]), + + ] + ) + + if input.workspace_id: + headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) + if input.org_id: + headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) + if input.if_modified_since is not None: + headers.extend(Fields([Field(name="if-modified-since", values=[serialize_rfc3339(ensure_utc(input.if_modified_since))])])) + return _HTTPRequest( + destination=_URI( + host="", + path=path, + scheme="https", + query=query, + ), + method="POST", + fields=headers, + body=body, + ) + async def _serialize_get_experiment_group(input: GetExperimentGroupInput, config: Config) -> HTTPRequest: if not input.id: raise ServiceError("id must not be empty.") @@ -1946,7 +1971,7 @@ async def _serialize_list_dimensions(input: ListDimensionsInput, config: Config) ) async def _serialize_list_experiment(input: ListExperimentInput, config: Config) -> HTTPRequest: - path = "/experiments" + path = "/experiments/list" query: str = f'' query_params: list[tuple[str, str | None]] = [] @@ -1978,12 +2003,23 @@ async def _serialize_list_experiment(input: ListExperimentInput, config: Config) query_params.append(("global_experiments_only", ('true' if input.global_experiments_only else 'false'))) if input.dimension_match_strategy is not None: query_params.append(("dimension_match_strategy", input.dimension_match_strategy)) + if input.prefix is not None: + query_params.extend(("prefix", e) for e in input.prefix) query = join_query_params(params=query_params, prefix=query) body: AsyncIterable[bytes] = AsyncBytesReader(b'') + codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS) + content = codec.serialize(input) + if not content: + content = b"{}" + content_length = len(content) + body = SeekableAsyncBytesReader(content) + headers = Fields( [ + Field(name="Content-Type", values=["application/json"]), + Field(name="Content-Length", values=[str(content_length)]), ] ) @@ -1992,6 +2028,8 @@ async def _serialize_list_experiment(input: ListExperimentInput, config: Config) headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) if input.org_id: headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) + if input.if_modified_since is not None: + headers.extend(Fields([Field(name="if-modified-since", values=[serialize_rfc3339(ensure_utc(input.if_modified_since))])])) return _HTTPRequest( destination=_URI( host="", @@ -1999,13 +2037,13 @@ async def _serialize_list_experiment(input: ListExperimentInput, config: Config) scheme="https", query=query, ), - method="GET", + method="POST", fields=headers, body=body, ) async def _serialize_list_experiment_groups(input: ListExperimentGroupsInput, config: Config) -> HTTPRequest: - path = "/experiment-groups" + path = "/experiment-groups/list" query: str = f'' query_params: list[tuple[str, str | None]] = [] @@ -2027,12 +2065,23 @@ async def _serialize_list_experiment_groups(input: ListExperimentGroupsInput, co query_params.append(("sort_by", input.sort_by)) if input.group_type is not None: query_params.extend(("group_type", e) for e in input.group_type) + if input.dimension_match_strategy is not None: + query_params.append(("dimension_match_strategy", input.dimension_match_strategy)) query = join_query_params(params=query_params, prefix=query) body: AsyncIterable[bytes] = AsyncBytesReader(b'') + codec = JSONCodec(default_timestamp_format=TimestampFormat.EPOCH_SECONDS) + content = codec.serialize(input) + if not content: + content = b"{}" + content_length = len(content) + body = SeekableAsyncBytesReader(content) + headers = Fields( [ + Field(name="Content-Type", values=["application/json"]), + Field(name="Content-Length", values=[str(content_length)]), ] ) @@ -2041,6 +2090,8 @@ async def _serialize_list_experiment_groups(input: ListExperimentGroupsInput, co headers.extend(Fields([Field(name="x-workspace", values=[input.workspace_id])])) if input.org_id: headers.extend(Fields([Field(name="x-org-id", values=[input.org_id])])) + if input.if_modified_since is not None: + headers.extend(Fields([Field(name="if-modified-since", values=[serialize_rfc3339(ensure_utc(input.if_modified_since))])])) return _HTTPRequest( destination=_URI( host="", @@ -2048,7 +2099,7 @@ async def _serialize_list_experiment_groups(input: ListExperimentGroupsInput, co scheme="https", query=query, ), - method="GET", + method="POST", fields=headers, body=body, ) diff --git a/crates/context_aware_config/Cargo.toml b/crates/context_aware_config/Cargo.toml index a735e9563..067d0cd9d 100644 --- a/crates/context_aware_config/Cargo.toml +++ b/crates/context_aware_config/Cargo.toml @@ -9,7 +9,6 @@ rust-version.workspace = true [dependencies] -actix-http = { workspace = true } actix-web = { workspace = true } anyhow = { workspace = true } bigdecimal = { workspace = true } diff --git a/crates/context_aware_config/src/api/config/handlers.rs b/crates/context_aware_config/src/api/config/handlers.rs index 7debd6d39..f50ca8a2a 100644 --- a/crates/context_aware_config/src/api/config/handlers.rs +++ b/crates/context_aware_config/src/api/config/handlers.rs @@ -9,9 +9,9 @@ use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper}; use itertools::Itertools; use serde_json::{Map, Value, json}; use service_utils::{ - helpers::fetch_dimensions_info_map, + helpers::{fetch_dimensions_info_map, is_not_modified}, redis::{CONFIG_KEY_SUFFIX, LAST_MODIFIED_KEY_SUFFIX, read_through_cache}, - service::types::{AppState, DbConnection, WorkspaceContext}, + service::types::{AppHeader, AppState, DbConnection, WorkspaceContext}, }; use superposition_core::{ ConfigFormat, JsonFormat, TomlFormat, @@ -43,9 +43,7 @@ use superposition_types::{ use crate::{ api::{ config::helpers::{ - add_config_version_to_header, add_last_modified_to_header, generate_config_from_version, get_config_version, get_max_created_at, - is_not_modified, }, context::{self, helpers::query_description}, }, @@ -485,23 +483,24 @@ async fn get_handler( state: Data, ) -> superposition::Result { let mut response = HttpResponse::Ok(); - let is_smithy = req.method() != actix_web::http::Method::GET; - let schema_name = &workspace_context.schema_name; + let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); + let max_created_at = read_through_cache::>( - format!("{}{LAST_MODIFIED_KEY_SUFFIX}", **schema_name), - schema_name, + format!( + "{}{LAST_MODIFIED_KEY_SUFFIX}", + *workspace_context.schema_name + ), + &workspace_context.schema_name, &state.redis, &state.db_pool, - |conn| get_max_created_at(conn, schema_name), + |conn| get_max_created_at(conn, &workspace_context.schema_name), ) .await .ok(); log::trace!("Max created at: {max_created_at:?}"); - let is_not_modified = is_not_modified(max_created_at, &req); - - if is_not_modified { + if is_not_modified(max_created_at, &req) { return Ok(HttpResponse::NotModified().finish()); } @@ -510,8 +509,11 @@ async fn get_handler( get_config_version(&query_filters.version, &workspace_context, &state).await?; let mut config = read_through_cache::( - format!("{}::{}{CONFIG_KEY_SUFFIX}", **schema_name, version), - schema_name, + format!( + "{}::{}{CONFIG_KEY_SUFFIX}", + *workspace_context.schema_name, version + ), + &workspace_context.schema_name, &state.redis, &state.db_pool, |conn| { @@ -541,25 +543,38 @@ async fn get_handler( if !context.is_empty() { config = config.filter_by_dimensions(&context); } - add_last_modified_to_header(max_created_at, is_smithy, &mut response); - add_config_version_to_header(&Some(version), &mut response); + AppHeader::add_last_modified(max_created_at, is_smithy, &mut response); + AppHeader::add_config_version(&Some(version), &mut response); Ok(response.json(config)) } /// Handler that returns config in TOML format with schema information. /// This uses generate_detailed_cac to fetch schemas from the database. #[authorized] +#[routes] #[get("/toml")] +#[post("/toml")] async fn get_toml_handler( req: HttpRequest, db_conn: DbConnection, workspace_context: WorkspaceContext, + state: Data, ) -> superposition::Result { let DbConnection(mut conn) = db_conn; + let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); - let max_created_at = get_max_created_at(&mut conn, &workspace_context.schema_name) - .map_err(|e| log::error!("failed to fetch max timestamp from event_log: {e}")) - .ok(); + let max_created_at = read_through_cache::>( + format!( + "{}{LAST_MODIFIED_KEY_SUFFIX}", + *workspace_context.schema_name + ), + &workspace_context.schema_name, + &state.redis, + &state.db_pool, + |conn| get_max_created_at(conn, &workspace_context.schema_name), + ) + .await + .ok(); log::info!("Max created at: {max_created_at:?}"); @@ -574,7 +589,7 @@ async fn get_toml_handler( .map_err(|e| unexpected_error!("Failed to serialize config to TOML: {}", e))?; let mut response = HttpResponse::Ok(); - add_last_modified_to_header(max_created_at, false, &mut response); + AppHeader::add_last_modified(max_created_at, is_smithy, &mut response); response.insert_header(("Content-Type", "application/toml")); Ok(response.body(toml_str)) @@ -583,17 +598,30 @@ async fn get_toml_handler( /// Handler that returns config in JSON format with schema information. /// This uses generate_detailed_cac to fetch schemas from the database. #[authorized] +#[routes] #[get("/json")] +#[post("/json")] async fn get_json_handler( req: HttpRequest, db_conn: DbConnection, workspace_context: WorkspaceContext, + state: Data, ) -> superposition::Result { let DbConnection(mut conn) = db_conn; + let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); - let max_created_at = get_max_created_at(&mut conn, &workspace_context.schema_name) - .map_err(|e| log::error!("failed to fetch max timestamp from event_log: {e}")) - .ok(); + let max_created_at = read_through_cache::>( + format!( + "{}{LAST_MODIFIED_KEY_SUFFIX}", + *workspace_context.schema_name + ), + &workspace_context.schema_name, + &state.redis, + &state.db_pool, + |conn| get_max_created_at(conn, &workspace_context.schema_name), + ) + .await + .ok(); log::info!("Max created at: {max_created_at:?}"); @@ -608,7 +636,7 @@ async fn get_json_handler( .map_err(|e| unexpected_error!("Failed to serialize config to JSON: {}", e))?; let mut response = HttpResponse::Ok(); - add_last_modified_to_header(max_created_at, false, &mut response); + AppHeader::add_last_modified(max_created_at, is_smithy, &mut response); response.insert_header(("Content-Type", "application/json")); Ok(response.body(json_str)) @@ -672,7 +700,7 @@ async fn resolve_handler( .await .map_err(|e| unexpected_error!("failed to generate config: {}", e))?; - let (is_smithy, query_data) = setup_query_data(&req, &body, &dimension_params)?; + let (is_smithy, query_data) = setup_query_data(&req, body, dimension_params)?; let resolved_config = { let mut conn = state.db_pool.get().map_err(|e| { @@ -692,8 +720,8 @@ async fn resolve_handler( }; let mut resp = HttpResponse::Ok(); - add_last_modified_to_header(max_created_at, is_smithy, &mut resp); - add_config_version_to_header(&Some(config_version), &mut resp); + AppHeader::add_last_modified(max_created_at, is_smithy, &mut resp); + AppHeader::add_config_version(&Some(config_version), &mut resp); Ok(resp.json(resolved_config)) } diff --git a/crates/context_aware_config/src/api/config/helpers.rs b/crates/context_aware_config/src/api/config/helpers.rs index 84af4b414..8f17aaca3 100644 --- a/crates/context_aware_config/src/api/config/helpers.rs +++ b/crates/context_aware_config/src/api/config/helpers.rs @@ -1,21 +1,20 @@ -use actix_http::header::HeaderValue; use actix_web::{ - HttpRequest, HttpResponseBuilder, + HttpRequest, web::{Data, Header, Json}, }; use cac_client::{eval_cac, eval_cac_with_reasoning}; -use chrono::{DateTime, Timelike, Utc}; +use chrono::{DateTime, Utc}; use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, dsl::max}; use serde_json::{Map, Value}; use service_utils::{ redis::{CONFIG_VERSION_KEY_SUFFIX, read_through_cache}, - service::types::{AppHeader, AppState, EncryptionKey, SchemaName, WorkspaceContext}, + service::types::{AppState, EncryptionKey, SchemaName, WorkspaceContext}, }; use superposition_macros::{bad_argument, db_error, unexpected_error}; use superposition_types::{ Config, DBConnection, api::config::{ContextPayload, MergeStrategy, ResolveConfigQuery}, - custom_query::{CommaSeparatedStringQParams, DimensionQuery, QueryMap}, + custom_query::{CommaSeparatedStringQParams, CustomQuery, DimensionQuery, QueryMap}, database::schema::config_versions::dsl as config_versions, result as superposition, }; @@ -70,40 +69,6 @@ pub async fn get_config_version( } } -pub fn add_last_modified_to_header( - max_created_at: Option>, - is_smithy: bool, - resp_builder: &mut HttpResponseBuilder, -) { - if let Some(date) = max_created_at { - let value = if is_smithy { - // Smithy needs to be in this format otherwise they can't - // deserialize it. - HeaderValue::from_str(date.to_rfc3339().as_str()) - } else { - HeaderValue::from_str(date.to_rfc2822().as_str()) - }; - if let Ok(header_value) = value { - resp_builder - .insert_header((AppHeader::LastModified.to_string(), header_value)); - } else { - log::error!("failed parsing datetime_utc {:?}", value); - } - } -} - -pub fn add_config_version_to_header( - config_version: &Option, - resp_builder: &mut HttpResponseBuilder, -) { - if let Some(val) = config_version { - resp_builder.insert_header(( - AppHeader::XConfigVersion.to_string(), - val.clone().to_string(), - )); - } -} - pub fn get_max_created_at( conn: &mut DBConnection, schema_name: &SchemaName, @@ -115,23 +80,6 @@ pub fn get_max_created_at( .and_then(|res| res.ok_or(diesel::result::Error::NotFound)) } -pub fn is_not_modified(max_created_at: Option>, req: &HttpRequest) -> bool { - let nanosecond_erasure = |t: DateTime| t.with_nanosecond(0); - let last_modified = req - .headers() - .get("If-Modified-Since") - .and_then(|header_val| { - let header_str = header_val.to_str().ok()?; - DateTime::parse_from_rfc2822(header_str) - .map(|datetime| datetime.with_timezone(&Utc)) - .ok() - }) - .and_then(nanosecond_erasure); - log::info!("last modified {last_modified:?}"); - let parsed_max: Option> = max_created_at.and_then(nanosecond_erasure); - max_created_at.is_some() && parsed_max <= last_modified -} - pub fn generate_config_from_version( version: &mut Option, conn: &mut DBConnection, @@ -175,22 +123,16 @@ pub fn generate_config_from_version( pub fn setup_query_data( req: &HttpRequest, - body: &Option>, - dimension_params: &DimensionQuery, + body: Option>, + dimension_params: DimensionQuery, ) -> superposition::Result<(bool, QueryMap)> { - let is_smithy: bool; + let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); let query_data = if req.method() == actix_web::http::Method::GET { - is_smithy = false; - (**dimension_params).clone() + dimension_params.into_inner() } else { - is_smithy = true; - body.as_ref() - .ok_or(bad_argument!( - "When using POST, context needs to be provided in the body." - ))? - .context - .clone() - .into() + body.map(|b| b.into_inner().context) + .map(Into::into) + .unwrap_or_default() }; Ok((is_smithy, query_data)) } diff --git a/crates/context_aware_config/src/api/context/handlers.rs b/crates/context_aware_config/src/api/context/handlers.rs index 9ecc25dc4..690549949 100644 --- a/crates/context_aware_config/src/api/context/handlers.rs +++ b/crates/context_aware_config/src/api/context/handlers.rs @@ -128,7 +128,7 @@ async fn create_handler( &state.master_encryption_key, )?; - let (put_response, version_id) = db_conn + let (put_response, config_version) = db_conn .transaction::<_, superposition::AppError, _>(|transaction_conn| { let put_response = operations::upsert( req, @@ -146,19 +146,19 @@ async fn create_handler( err })?; - let version_id = add_config_version( + let config_version = add_config_version( &state, tags, req_change_reason.into(), transaction_conn, &workspace_context.schema_name, )?; - Ok((put_response, version_id)) + Ok((put_response, config_version)) })?; let DbConnection(mut conn) = db_conn; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -169,7 +169,7 @@ async fn create_handler( payload: &put_response, resource: Resource::Context, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Create, }; @@ -187,7 +187,7 @@ async fn create_handler( http_resp.insert_header(( AppHeader::XConfigVersion.to_string(), - version_id.to_string(), + config_version.id.to_string(), )); Ok(http_resp.json(put_response)) @@ -242,7 +242,7 @@ async fn update_handler( &state.master_encryption_key, )?; - let (override_resp, version_id) = db_conn + let (override_resp, config_version) = db_conn .transaction::<_, superposition::AppError, _>(|transaction_conn| { let override_resp = operations::update( &workspace_context, @@ -256,19 +256,19 @@ async fn update_handler( err })?; - let version_id = add_config_version( + let config_version = add_config_version( &state, tags, req_change_reason.into(), transaction_conn, &workspace_context.schema_name, )?; - Ok((override_resp, version_id)) + Ok((override_resp, config_version)) })?; let DbConnection(mut conn) = db_conn; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -279,7 +279,7 @@ async fn update_handler( payload: &override_resp, resource: Resource::Context, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Update, }; @@ -297,7 +297,7 @@ async fn update_handler( http_resp.insert_header(( AppHeader::XConfigVersion.to_string(), - version_id.to_string(), + config_version.id.to_string(), )); Ok(http_resp.json(override_resp)) @@ -370,7 +370,7 @@ async fn move_handler( &state.master_encryption_key, )?; - let (move_response, version_id) = db_conn + let (move_response, config_version) = db_conn .transaction::<_, superposition::AppError, _>(|transaction_conn| { let move_response = operations::r#move( &workspace_context, @@ -387,7 +387,7 @@ async fn move_handler( log::error!("move api failed with error: {:?}", err); err })?; - let version_id = add_config_version( + let config_version = add_config_version( &state, tags, move_response.context.change_reason.clone().into(), @@ -395,13 +395,13 @@ async fn move_handler( &workspace_context.schema_name, )?; - Ok((move_response, version_id)) + Ok((move_response, config_version)) })?; let DbConnection(mut conn) = db_conn; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -412,7 +412,7 @@ async fn move_handler( payload: vec![&move_response.deleted_context, &move_response.context], resource: Resource::Context, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Batch(vec![Action::Delete, move_response.action]), }; @@ -430,7 +430,7 @@ async fn move_handler( http_resp.insert_header(( AppHeader::XConfigVersion.to_string(), - version_id.to_string(), + config_version.id.to_string(), )); Ok(http_resp.json(move_response.context)) @@ -662,7 +662,7 @@ async fn delete_handler( .await?; let tags = parse_config_tags(custom_headers.config_tags)?; - let (version_id, deleted_ctx) = db_conn + let (config_version, deleted_ctx) = db_conn .transaction::<_, superposition::AppError, _>(|transaction_conn| { contexts_table .filter(context_id.eq(ctx_id.clone())) @@ -677,19 +677,19 @@ async fn delete_handler( let config_version_desc = Description::try_from(format!("Deleted context by {}", user.get_email())) .map_err(|e| unexpected_error!(e))?; - let version_id = add_config_version( + let config_version = add_config_version( &state, tags, config_version_desc, transaction_conn, &workspace_context.schema_name, )?; - Ok((version_id, deleted_ctx)) + Ok((config_version, deleted_ctx)) })?; let DbConnection(mut conn) = db_conn; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -699,7 +699,7 @@ async fn delete_handler( payload: &deleted_ctx, resource: Resource::Context, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Delete, }; @@ -718,7 +718,7 @@ async fn delete_handler( Ok(http_resp .insert_header(( AppHeader::XConfigVersion.to_string().as_str(), - version_id.to_string().as_str(), + config_version.id.to_string().as_str(), )) .finish()) } @@ -770,24 +770,19 @@ async fn bulk_operations_handler( use contexts::dsl::contexts; let DbConnection(mut conn) = db_conn; - let mut is_v2 = false; + let is_v2 = matches!(req, Either::Right(_)); let ops = match req { Either::Left(o) => o.into_inner(), - Either::Right(bo) => { - is_v2 = true; - bo.into_inner().operations - } + Either::Right(bo) => bo.into_inner().operations, }; bulk_authorized(&_auth_z, &ops, &workspace_context.schema_name, &mut conn).await?; - // Marking immutable. - let is_v2 = is_v2; let mut all_change_reasons = Vec::new(); let mut webhook_actions: Vec = Vec::new(); let mut webhook_contexts: Vec = Vec::new(); let tags = parse_config_tags(custom_headers.config_tags)?; - let (response, version_id) = + let (response, config_version) = conn.transaction::<_, superposition::AppError, _>(|transaction_conn| { let mut response = Vec::::new(); for action in ops.into_iter() { @@ -950,7 +945,7 @@ async fn bulk_operations_handler( } } - let version_id = add_config_version( + let config_version = add_config_version( &state, tags, Description::try_from_change_reasons(all_change_reasons) @@ -958,11 +953,11 @@ async fn bulk_operations_handler( transaction_conn, &workspace_context.schema_name, )?; - Ok((response, version_id)) + Ok((response, config_version)) })?; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -973,7 +968,7 @@ async fn bulk_operations_handler( payload: &webhook_contexts, resource: Resource::Context, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Batch(webhook_actions), }; @@ -990,7 +985,7 @@ async fn bulk_operations_handler( }; resp_builder.insert_header(( AppHeader::XConfigVersion.to_string(), - version_id.to_string(), + config_version.id.to_string(), )); let http_resp = if is_v2 { @@ -1056,7 +1051,7 @@ async fn weight_recompute_handler( // Update database and add config version let last_modified_time = Utc::now(); - let config_version_id = + let config_version = conn.transaction::<_, superposition::AppError, _>(|transaction_conn| { for (context_weight, context_id) in contexts_new_weight.clone() { diesel::update(contexts.filter(id.eq(context_id))) @@ -1075,11 +1070,10 @@ async fn weight_recompute_handler( })?; } let config_version_desc = Description::try_from("Recomputed weight".to_string()).map_err(|e| unexpected_error!(e))?; - let version_id = add_config_version(&state, tags, config_version_desc, transaction_conn, &workspace_context.schema_name)?; - Ok(version_id) + add_config_version(&state, tags, config_version_desc, transaction_conn, &workspace_context.schema_name) })?; let _ = put_config_in_redis( - config_version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -1090,7 +1084,7 @@ async fn weight_recompute_handler( payload: &response, resource: Resource::Context, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(config_version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Update, }; @@ -1107,7 +1101,7 @@ async fn weight_recompute_handler( }; http_resp.insert_header(( AppHeader::XConfigVersion.to_string(), - config_version_id.to_string(), + config_version.id.to_string(), )); Ok(http_resp.json(ListResponse::new(response))) } diff --git a/crates/context_aware_config/src/api/default_config/handlers.rs b/crates/context_aware_config/src/api/default_config/handlers.rs index ad918f328..5e1e0df40 100644 --- a/crates/context_aware_config/src/api/default_config/handlers.rs +++ b/crates/context_aware_config/src/api/default_config/handlers.rs @@ -152,7 +152,7 @@ async fn create_handler( &workspace_context.schema_name, )?; - let version_id = + let config_version = conn.transaction::<_, superposition::AppError, _>(|transaction_conn| { diesel::insert_into(dsl::default_configs) .values(&default_config) @@ -160,18 +160,18 @@ async fn create_handler( .schema_name(&workspace_context.schema_name) .execute(transaction_conn)?; - let version_id = add_config_version( + let config_version = add_config_version( &state, tags, req.change_reason.into(), transaction_conn, &workspace_context.schema_name, )?; - Ok(version_id) + Ok(config_version) })?; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -182,7 +182,7 @@ async fn create_handler( payload: &default_config, resource: Resource::DefaultConfig, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Create, }; @@ -200,7 +200,7 @@ async fn create_handler( http_resp.insert_header(( AppHeader::XConfigVersion.to_string(), - version_id.to_string(), + config_version.id.to_string(), )); Ok(http_resp.json(default_config)) @@ -304,7 +304,7 @@ async fn update_handler( )?; } - let (db_row, version_id) = + let (db_row, config_version) = conn.transaction::<_, superposition::AppError, _>(|transaction_conn| { let change_reason = req.change_reason.clone(); let val = diesel::update(dsl::default_configs) @@ -317,7 +317,7 @@ async fn update_handler( .schema_name(&workspace_context.schema_name) .get_result::(transaction_conn)?; - let version_id = add_config_version( + let config_version = add_config_version( &state, tags.clone(), change_reason.into(), @@ -325,11 +325,11 @@ async fn update_handler( &workspace_context.schema_name, )?; - Ok((val, version_id)) + Ok((val, config_version)) })?; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -340,7 +340,7 @@ async fn update_handler( payload: &db_row, resource: Resource::DefaultConfig, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Update, }; @@ -357,7 +357,7 @@ async fn update_handler( }; http_resp.insert_header(( AppHeader::XConfigVersion.to_string(), - version_id.to_string(), + config_version.id.to_string(), )); Ok(http_resp.json(db_row)) } @@ -523,7 +523,7 @@ async fn delete_handler( get_key_usage_context_ids(&key, &mut conn, &workspace_context.schema_name) .map_err(|_| unexpected_error!("Something went wrong"))?; if context_ids.is_empty() { - let (version_id, default_config) = conn + let (config_version, default_config) = conn .transaction::<_, superposition::AppError, _>(|transaction_conn| { diesel::update(dsl::default_configs) .filter(dsl::key.eq(&key)) @@ -549,7 +549,7 @@ async fn delete_handler( user.get_email() )) .map_err(|e| unexpected_error!(e))?; - let version_id = add_config_version( + let config_version = add_config_version( &state, tags, config_version_desc, @@ -560,13 +560,13 @@ async fn delete_handler( "default config key: {key} deleted by {}", user.get_email() ); - Ok((version_id, default_config)) + Ok((config_version, default_config)) } } })?; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -577,7 +577,7 @@ async fn delete_handler( payload: &default_config, resource: Resource::DefaultConfig, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Delete, }; @@ -594,7 +594,7 @@ async fn delete_handler( }; http_resp.insert_header(( AppHeader::XConfigVersion.to_string(), - version_id.to_string(), + config_version.id.to_string(), )); Ok(http_resp.finish()) diff --git a/crates/context_aware_config/src/api/dimension/handlers.rs b/crates/context_aware_config/src/api/dimension/handlers.rs index 172721dff..6f8e5e5df 100644 --- a/crates/context_aware_config/src/api/dimension/handlers.rs +++ b/crates/context_aware_config/src/api/dimension/handlers.rs @@ -168,8 +168,8 @@ async fn create_handler( dimension_type: create_req.dimension_type, }; - let (inserted_dimension, is_mandatory, version_id) = conn - .transaction::<_, superposition::AppError, _>(|transaction_conn| { + let (inserted_dimension, is_mandatory, config_version) = + conn.transaction::<_, superposition::AppError, _>(|transaction_conn| { diesel::update(dimensions::table) .filter(dimensions::position.ge(dimension_data.position)) .set(( @@ -213,14 +213,14 @@ async fn create_handler( .unwrap_or_default() .contains(&inserted_dimension.dimension); - let version_id = add_config_version( + let config_version = add_config_version( &state, tags, dimension_data.change_reason.into(), transaction_conn, &workspace_context.schema_name, )?; - Ok((inserted_dimension, is_mandatory, version_id)) + Ok((inserted_dimension, is_mandatory, config_version)) } Err(diesel::result::Error::DatabaseError( diesel::result::DatabaseErrorKind::ForeignKeyViolation, @@ -244,7 +244,7 @@ async fn create_handler( })?; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -255,7 +255,7 @@ async fn create_handler( payload: &inserted_dimension, resource: Resource::Dimension, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Create, }; @@ -272,7 +272,7 @@ async fn create_handler( }; http_resp.insert_header(( AppHeader::XConfigVersion.to_string(), - version_id.to_string(), + config_version.id.to_string(), )); Ok(http_resp.json(DimensionResponse::new(inserted_dimension, is_mandatory))) } @@ -395,7 +395,7 @@ async fn update_handler( let update_change_reason = update_req.change_reason.clone(); - let (result, is_mandatory, version_id) = conn + let (result, is_mandatory, config_version) = conn .transaction::<_, superposition::AppError, _>(|transaction_conn| { if let Some(position_val) = update_req.position { let new_position = position_val; @@ -469,7 +469,7 @@ async fn update_handler( .unwrap_or_default() .contains(&result.dimension); - let version_id = add_config_version( + let config_version = add_config_version( &state, tags, update_change_reason.into(), @@ -477,11 +477,11 @@ async fn update_handler( &workspace_context.schema_name, )?; - Ok((result, is_mandatory, version_id)) + Ok((result, is_mandatory, config_version)) })?; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -492,7 +492,7 @@ async fn update_handler( payload: &result, resource: Resource::Dimension, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Update, }; @@ -509,7 +509,7 @@ async fn update_handler( }; http_resp.insert_header(( AppHeader::XConfigVersion.to_string(), - version_id.to_string(), + config_version.id.to_string(), )); Ok(http_resp.json(DimensionResponse::new(result, is_mandatory))) } @@ -598,7 +598,7 @@ async fn delete_handler( )?; if context_ids.is_empty() { - let (version_id, dimension_data) = conn.transaction::<_, superposition::AppError, _>(|transaction_conn| { + let (config_version, dimension_data) = conn.transaction::<_, superposition::AppError, _>(|transaction_conn| { use dimensions::dsl; if !dimension_data.dependency_graph.is_empty() { @@ -651,7 +651,7 @@ async fn delete_handler( user.get_email() )) .map_err(|e| unexpected_error!(e))?; - let version_id = add_config_version( + let config_version = add_config_version( &state, tags, config_version_desc, @@ -662,13 +662,13 @@ async fn delete_handler( "Dimension: {name} deleted by {}", user.get_email() ); - Ok((version_id, dimension_data)) + Ok((config_version, dimension_data)) } } })?; let _ = put_config_in_redis( - version_id, + &config_version, &state, &workspace_context.schema_name, &mut conn, @@ -678,7 +678,7 @@ async fn delete_handler( payload: &dimension_data, resource: Resource::Dimension, event: WebhookEvent::ConfigChanged, - config_version_opt: Some(version_id.to_string()), + config_version_opt: Some(config_version.id.to_string()), action: Action::Delete, }; @@ -695,7 +695,7 @@ async fn delete_handler( }; http_resp.insert_header(( AppHeader::XConfigVersion.to_string(), - version_id.to_string(), + config_version.id.to_string(), )); Ok(http_resp.finish()) diff --git a/crates/context_aware_config/src/helpers.rs b/crates/context_aware_config/src/helpers.rs index b9495cf56..981c55abe 100644 --- a/crates/context_aware_config/src/helpers.rs +++ b/crates/context_aware_config/src/helpers.rs @@ -4,7 +4,7 @@ use actix_web::{ http::header::{HeaderMap, HeaderName, HeaderValue}, web::Data, }; -use chrono::{DateTime, Utc}; +use chrono::Utc; use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper}; use fred::{interfaces::KeysInterface, types::Expiration}; use serde_json::{Map, Value, json}; @@ -28,8 +28,8 @@ use superposition_types::{ models::{ ChangeReason, Description, cac::{ - ConfigVersion, DependencyGraph, DimensionType, FunctionCode, - FunctionRuntimeVersion, FunctionType, + ConfigVersion, ConfigVersionListItem, DependencyGraph, DimensionType, + FunctionCode, FunctionRuntimeVersion, FunctionType, }, }, schema::{ @@ -198,7 +198,7 @@ pub fn add_config_version( description: Description, db_conn: &mut DBConnection, schema_name: &SchemaName, -) -> superposition::Result { +) -> superposition::Result { use config_versions::dsl::config_versions; let version_id = generate_snowflake_id(state)?; let config = generate_cac(db_conn, schema_name)?; @@ -219,11 +219,11 @@ pub fn add_config_version( .schema_name(schema_name) .execute(db_conn)?; - Ok(version_id) + Ok(ConfigVersionListItem::from(config_version)) } pub async fn put_config_in_redis( - version_id: i64, + config_version: &ConfigVersionListItem, state: &Data, schema_name: &SchemaName, db_conn: &mut DBConnection, @@ -243,10 +243,13 @@ pub async fn put_config_in_redis( log::error!("failed to convert cac config to string: {}", e); unexpected_error!("could not convert cac config to string") })?; - let config_key = format!("{}::{}{CONFIG_KEY_SUFFIX}", **schema_name, version_id); + let config_key = format!( + "{}::{}{CONFIG_KEY_SUFFIX}", + **schema_name, config_version.id + ); let last_modified_at_key = format!("{}{LAST_MODIFIED_KEY_SUFFIX}", **schema_name); let config_version_key = format!("{}{CONFIG_VERSION_KEY_SUFFIX}", **schema_name); - let last_modified = DateTime::to_rfc2822(&Utc::now()); + redis_pool .set::<(), String, String>( config_key, @@ -263,7 +266,7 @@ pub async fn put_config_in_redis( redis_pool .set::<(), String, String>( last_modified_at_key, - last_modified, + config_version.created_at.to_rfc2822(), expiration.clone(), None, false, @@ -274,11 +277,17 @@ pub async fn put_config_in_redis( unexpected_error!("failed to set last_modified_key in redis") })?; redis_pool - .set::<(), String, i64>(config_version_key, version_id, expiration, None, false) + .set::<(), String, i64>( + config_version_key, + config_version.id, + expiration, + None, + false, + ) .await .map_err(|e| { log::warn!("failed to set config_version_key in redis: {}", e); - unexpected_error!("failed to set config_version_keyx in redis") + unexpected_error!("failed to set config_version_key in redis") })?; Ok(()) } diff --git a/crates/experimentation_client/src/interface.rs b/crates/experimentation_client/src/interface.rs index 2d6abcbb7..5d58aaeae 100644 --- a/crates/experimentation_client/src/interface.rs +++ b/crates/experimentation_client/src/interface.rs @@ -306,7 +306,7 @@ pub extern "C" fn expt_get_filtered_satisfied_experiments( let prefix_list: Vec = filter_string.split(',').map(String::from).collect(); - Some(prefix_list).filter(|list| !list.is_empty()) + Some(prefix_list) }; let context = evaluate_local_cohorts_skip_unresolved(&dimensions, &context); diff --git a/crates/experimentation_client/src/lib.rs b/crates/experimentation_client/src/lib.rs index 9d05449a3..2416f0ea9 100644 --- a/crates/experimentation_client/src/lib.rs +++ b/crates/experimentation_client/src/lib.rs @@ -16,10 +16,11 @@ use superposition_types::{ api::experiments::ExperimentListFilters, custom_query::{CommaSeparatedQParams, PaginationParams, QueryParam}, database::models::experimentation::{ - Bucket, ExperimentGroup, ExperimentStatusType, GroupType, Variant, + Bucket, ExperimentGroup, ExperimentStatusType, GroupType, }, + experimental::{Experimental, ExperimentalVariants}, logic::evaluate_local_cohorts, - DimensionInfo, Overridden, PaginatedResponse, + DimensionInfo, PaginatedResponse, }; pub use superposition_types::{ api::experiments::ExperimentResponse, database::models::experimentation::Variants, @@ -148,23 +149,25 @@ impl Client { context: &Map, prefix: Option>, ) -> Result { - let running_experiments = self + let mut experiments = self .experiments .read() .await - .iter() - .filter(|(_, exp)| superposition_types::apply(&exp.context, context)) - .map(|(_, exp)| exp.clone()) + .values() + .cloned() .collect::(); - if let Some(prefix_list) = prefix { - return Ok(Self::filter_experiments_by_prefix( - running_experiments, - prefix_list, - )); + if let Some(prefix_list) = prefix.filter(|p| !p.is_empty()) { + let prefix_set: HashSet = HashSet::from_iter(prefix_list); + experiments = + ExperimentResponse::filter_keys_by_prefix(experiments, &prefix_set); } - Ok(running_experiments) + if !context.is_empty() { + experiments = ExperimentResponse::get_satisfied(experiments, context); + } + + Ok(experiments) } pub async fn get_filtered_satisfied_experiments( @@ -172,28 +175,25 @@ impl Client { context: &Map, prefix: Option>, ) -> Result { - let experiments = self.experiments.read().await; + let mut experiments = self + .experiments + .read() + .await + .values() + .cloned() + .collect::(); - let filtered_running_experiments = experiments - .iter() - .filter_map(|(_, exp)| { - if exp.context.is_empty() { - Some(exp.clone()) - } else { - superposition_types::partial_apply(&exp.context, context) - .then(|| exp.clone()) - } - }) - .collect::>(); + if let Some(prefix_list) = prefix.filter(|p| !p.is_empty()) { + let prefix_list: HashSet = HashSet::from_iter(prefix_list); + experiments = + ExperimentResponse::filter_keys_by_prefix(experiments, &prefix_list); + } - if let Some(prefix_list) = prefix { - return Ok(Self::filter_experiments_by_prefix( - filtered_running_experiments, - prefix_list, - )); + if !context.is_empty() { + experiments = ExperimentResponse::filter_by_eval(experiments, context); } - Ok(filtered_running_experiments) + Ok(experiments) } pub async fn get_running_experiments(&self) -> Result { @@ -201,39 +201,6 @@ impl Client { let experiments: Experiments = running_experiments.values().cloned().collect(); Ok(experiments) } - - fn filter_experiments_by_prefix( - experiments: Vec, - prefix_list: Vec, - ) -> Vec { - let prefix_list: HashSet = HashSet::from_iter(prefix_list); - experiments - .into_iter() - .filter_map(|experiment| { - let variants: Vec<_> = experiment - .variants - .into_iter() - .filter_map(|mut variant| { - Variant::filter_keys_by_prefix(&variant, &prefix_list) - .map(|filtered_overrides_map| { - variant.overrides = filtered_overrides_map; - variant - }) - .ok() - }) - .collect(); - - if !variants.is_empty() { - Some(ExperimentResponse { - variants: Variants::new(variants), - ..experiment - }) - } else { - None // Skip this experiment - } - }) - .collect() - } } pub fn get_applicable_buckets_from_group( @@ -329,6 +296,7 @@ async fn get_experiments( sort_on: None, sort_by: None, global_experiments_only: None, + prefix: None, dimension_match_strategy: None, }; let pagination_params = PaginationParams::all_entries(); diff --git a/crates/experimentation_platform/Cargo.toml b/crates/experimentation_platform/Cargo.toml index ba88f9cf1..1138b8306 100644 --- a/crates/experimentation_platform/Cargo.toml +++ b/crates/experimentation_platform/Cargo.toml @@ -32,6 +32,7 @@ superposition_types = { workspace = true, features = [ "diesel_derives", "server", ] } +tokio = { workspace = true } [features] disable_db_data_validation = ["superposition_types/disable_db_data_validation"] diff --git a/crates/experimentation_platform/src/api.rs b/crates/experimentation_platform/src/api.rs index acf59285e..ae344d88b 100644 --- a/crates/experimentation_platform/src/api.rs +++ b/crates/experimentation_platform/src/api.rs @@ -1,2 +1,3 @@ +pub mod experiment_config; pub mod experiment_groups; pub mod experiments; diff --git a/crates/experimentation_platform/src/api/experiment_config.rs b/crates/experimentation_platform/src/api/experiment_config.rs new file mode 100644 index 000000000..cd5f70688 --- /dev/null +++ b/crates/experimentation_platform/src/api/experiment_config.rs @@ -0,0 +1,2 @@ +pub mod handlers; +pub use handlers::endpoints; diff --git a/crates/experimentation_platform/src/api/experiment_config/handlers.rs b/crates/experimentation_platform/src/api/experiment_config/handlers.rs new file mode 100644 index 000000000..daf1f20dc --- /dev/null +++ b/crates/experimentation_platform/src/api/experiment_config/handlers.rs @@ -0,0 +1,210 @@ +use std::collections::HashSet; + +use actix_web::{ + HttpRequest, HttpResponse, Scope, routes, + web::{Data, Json}, +}; +use chrono::{DateTime, Utc}; +use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl}; +use service_utils::{ + db::run_query, + helpers::is_not_modified, + redis::{ + EXPERIMENT_CONFIG_LAST_MODIFIED_KEY_SUFFIX, EXPERIMENT_GROUPS_LIST_KEY_SUFFIX, + EXPERIMENTS_LIST_KEY_SUFFIX, read_through_cache, + }, + service::types::{AppHeader, AppState, WorkspaceContext}, +}; +use superposition_derives::{authorized, declare_resource}; +use superposition_types::{ + DBConnection, IsEmpty, PaginatedResponse, + api::{ + DimensionMatchStrategy, + experiment_config::{ + ExperimentConfig, ExperimentConfigFilters, ExperimentConfigRequest, + }, + experiments::ExperimentResponse, + }, + custom_query::{self as superposition_query, CustomQuery, DimensionQuery, QueryMap}, + database::{ + models::experimentation::{Experiment, ExperimentGroup, ExperimentStatusType}, + schema::{ + event_log::dsl as event_log, experiment_groups::dsl as experiment_groups, + experiments::dsl as experiments, + }, + }, + experimental::{Experimental, ExperimentalVariants}, + result as superposition, +}; +use tokio::join; + +declare_resource!(ExperimentConfig); + +pub fn endpoints(scope: Scope) -> Scope { + scope.service(get_handler) +} + +#[allow(clippy::too_many_arguments)] +#[authorized] +#[routes] +#[get("")] +#[post("")] +async fn get_handler( + workspace_context: WorkspaceContext, + req: HttpRequest, + body: Option>, + filters: superposition_query::Query, + dimension_params: DimensionQuery, + state: Data, +) -> superposition::Result { + let max_event_timestamp = read_through_cache::>>( + format!( + "{}{EXPERIMENT_CONFIG_LAST_MODIFIED_KEY_SUFFIX}", + *workspace_context.schema_name + ), + &workspace_context.schema_name, + &state.redis, + &state.db_pool, + |conn| { + event_log::event_log + .filter( + event_log::table_name.eq_any(["experiments", "experiment_groups"]), + ) + .select(diesel::dsl::max(event_log::timestamp)) + .schema_name(&workspace_context.schema_name) + .first(conn) + }, + ) + .await?; + + if is_not_modified(max_event_timestamp, &req) { + return Ok(HttpResponse::NotModified().finish()); + }; + + let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); + let dimension_params = if req.method() == actix_web::http::Method::GET { + dimension_params.into_inner() + } else { + body.and_then(|b| b.into_inner().context) + .map(Into::into) + .unwrap_or_default() + }; + + let read_from_redis = dimension_params.is_empty() && filters.is_empty(); + + let mut response = HttpResponse::Ok(); + AppHeader::add_last_modified(max_event_timestamp, is_smithy, &mut response); + + if read_from_redis { + let exp_list_ft = read_through_cache::>( + format!( + "{}{EXPERIMENTS_LIST_KEY_SUFFIX}", + *workspace_context.schema_name + ), + &workspace_context.schema_name, + &state.redis, + &state.db_pool, + |conn| { + let experiment_responses = experiments::experiments + .filter( + experiments::status.eq_any(ExperimentStatusType::active_list()), + ) + .order(experiments::last_modified.desc()) + .schema_name(&workspace_context.schema_name) + .load::(conn)? + .into_iter() + .map(ExperimentResponse::from) + .collect(); + + Ok(PaginatedResponse::all(experiment_responses)) + }, + ); + + let exp_group_list_ft = read_through_cache::>( + format!( + "{}{EXPERIMENT_GROUPS_LIST_KEY_SUFFIX}", + *workspace_context.schema_name + ), + &workspace_context.schema_name, + &state.redis, + &state.db_pool, + |conn| { + experiment_groups::experiment_groups + .order(experiment_groups::last_modified_at.desc()) + .schema_name(&workspace_context.schema_name) + .load::(conn) + }, + ); + + let (exp_list, exp_group_list) = join!(exp_list_ft, exp_group_list_ft); + + Ok(ExperimentConfig { + experiments: exp_list?.data, + experiment_groups: exp_group_list?, + }) + } else { + run_query(&state.db_pool, |conn| { + get_experiment_config_db(filters, dimension_params, conn, &workspace_context) + }) + } + .map(|r| response.json(r)) +} + +fn get_experiment_config_db( + filters: superposition_query::Query, + dimension_params: QueryMap, + conn: &mut DBConnection, + workspace_context: &WorkspaceContext, +) -> superposition::DieselResult { + let filters = filters.into_inner(); + + let exp_list = { + let mut experiment_list: Vec = experiments::experiments + .filter(experiments::status.eq_any(ExperimentStatusType::active_list())) + .order(experiments::last_modified.desc()) + .schema_name(&workspace_context.schema_name) + .load::(conn)?; + + if let Some(prefix_list) = filters.prefix.filter(|p| !p.is_empty()) { + experiment_list = Experiment::filter_keys_by_prefix( + experiment_list, + &HashSet::from_iter(prefix_list.0), + ); + } + + if !dimension_params.is_empty() { + let filter_fn = match filters.dimension_match_strategy.unwrap_or_default() { + DimensionMatchStrategy::Exact => Experiment::get_satisfied, + DimensionMatchStrategy::Subset => Experiment::filter_by_eval, + }; + experiment_list = filter_fn(experiment_list, &dimension_params); + } + + experiment_list + .into_iter() + .map(ExperimentResponse::from) + .collect() + }; + + let exp_group_list = { + let mut group_list = experiment_groups::experiment_groups + .order(experiment_groups::last_modified_at.desc()) + .schema_name(&workspace_context.schema_name) + .load::(conn)?; + + if !dimension_params.is_empty() { + let filter_fn = match filters.dimension_match_strategy.unwrap_or_default() { + DimensionMatchStrategy::Exact => ExperimentGroup::get_satisfied, + DimensionMatchStrategy::Subset => ExperimentGroup::filter_by_eval, + }; + group_list = filter_fn(group_list, &dimension_params); + } + + group_list + }; + + Ok(ExperimentConfig { + experiments: exp_list, + experiment_groups: exp_group_list, + }) +} diff --git a/crates/experimentation_platform/src/api/experiment_groups/handlers.rs b/crates/experimentation_platform/src/api/experiment_groups/handlers.rs index 52e1514ef..55fa8fa47 100644 --- a/crates/experimentation_platform/src/api/experiment_groups/handlers.rs +++ b/crates/experimentation_platform/src/api/experiment_groups/handlers.rs @@ -1,8 +1,10 @@ +use std::cmp::min; + use actix_web::{ - Scope, delete, get, patch, post, + HttpRequest, HttpResponse, Scope, delete, get, patch, post, routes, web::{self, Data, Json}, }; -use chrono::Utc; +use chrono::{DateTime, Utc}; use diesel::{ Connection, ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper, TextExpressionMethods, @@ -10,19 +12,31 @@ use diesel::{ use serde_json::Value; use service_utils::{ db::run_query, - helpers::{generate_snowflake_id, get_from_env_or_default}, - redis::{EXPERIMENT_GROUPS_LIST_KEY_SUFFIX, read_through_cache}, - service::types::{AppState, DbConnection, WorkspaceContext}, + helpers::{ + fetch_dimensions_info_map, generate_snowflake_id, get_from_env_or_default, + is_not_modified, + }, + redis::{ + EXPERIMENT_GROUPS_LAST_MODIFIED_KEY_SUFFIX, EXPERIMENT_GROUPS_LIST_KEY_SUFFIX, + read_through_cache, + }, + service::types::{AppHeader, AppState, DbConnection, WorkspaceContext}, }; use superposition_derives::{authorized, declare_resource}; use superposition_macros::{bad_argument, unexpected_error}; use superposition_types::{ - DBConnection, IsEmpty, PaginatedResponse, SortBy, User, - api::experiment_groups::{ - ExpGroupCreateRequest, ExpGroupFilters, ExpGroupMemberRequest, - ExpGroupUpdateRequest, SortOn, + Contextual, DBConnection, IsEmpty, PaginatedResponse, SortBy, User, + api::{ + DimensionMatchStrategy, + experiment_groups::{ + ExpGroupCreateRequest, ExpGroupFilters, ExpGroupListRequest, + ExpGroupMemberRequest, ExpGroupUpdateRequest, SortOn, + }, + }, + custom_query::{ + self as superposition_query, CustomQuery, DimensionQuery, PaginationParams, + QueryMap, }, - custom_query::{self as superposition_query, CustomQuery, PaginationParams}, database::{ models::{ ChangeReason, @@ -32,9 +46,11 @@ use superposition_types::{ }, }, schema::{ - experiment_groups::dsl as experiment_groups, experiments::dsl as experiments, + event_log, experiment_groups::dsl as experiment_groups, + experiments::dsl as experiments, }, }, + logic::evaluate_local_cohorts_skip_unresolved, result as superposition, }; @@ -307,19 +323,63 @@ async fn remove_members_handler( Ok(experiment_group) } +#[allow(clippy::too_many_arguments)] #[authorized] +#[routes] #[get("")] +#[post("/list")] async fn list_handler( workspace_context: WorkspaceContext, pagination_params: superposition_query::Query, filters: superposition_query::Query, + dimension_params: DimensionQuery, + body: Option>, state: Data, -) -> superposition::Result>> { + req: HttpRequest, +) -> superposition::Result { + let max_event_timestamp = read_through_cache::>>( + format!( + "{}{EXPERIMENT_GROUPS_LAST_MODIFIED_KEY_SUFFIX}", + *workspace_context.schema_name + ), + &workspace_context.schema_name, + &state.redis, + &state.db_pool, + |conn| { + event_log::table + .filter(event_log::table_name.eq("experiment_groups")) + .select(diesel::dsl::max(event_log::timestamp)) + .schema_name(&workspace_context.schema_name) + .first(conn) + }, + ) + .await?; + + if is_not_modified(max_event_timestamp, &req) { + return Ok(HttpResponse::NotModified().finish()); + }; + + let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); + let dimension_params = if req.method() == actix_web::http::Method::GET { + dimension_params.into_inner() + } else { + body.and_then(|b| b.into_inner().context) + .map(Into::into) + .unwrap_or_default() + }; + let key = format!( "{}{}", *workspace_context.schema_name, EXPERIMENT_GROUPS_LIST_KEY_SUFFIX ); - let read_from_redis = pagination_params.all.is_some_and(|e| e) && filters.is_empty(); + + let read_from_redis = pagination_params.all.is_some_and(|e| e) + && filters.is_empty() + && dimension_params.is_empty(); + + let mut response = HttpResponse::Ok(); + AppHeader::add_last_modified(max_event_timestamp, is_smithy, &mut response); + if read_from_redis { read_through_cache::>( key, @@ -330,30 +390,31 @@ async fn list_handler( list_experiment_groups_db( &pagination_params, filters, + dimension_params, conn, &workspace_context, ) }, ) .await - .map(Json) - .map_err(|e| unexpected_error!(e)) } else { run_query(&state.db_pool, |conn| { list_experiment_groups_db( &pagination_params, filters, + dimension_params, conn, &workspace_context, ) }) - .map(Json) } + .map(|r| response.json(r)) } fn list_experiment_groups_db( pagination_params: &superposition_query::Query, filters: superposition_query::Query, + dimension_params: QueryMap, conn: &mut DBConnection, workspace_context: &WorkspaceContext, ) -> superposition::DieselResult> { @@ -383,6 +444,10 @@ fn list_experiment_groups_db( let count_query = query_builder(&filters); let sort_by = filters.sort_by.unwrap_or(SortBy::Desc); let sort_on = filters.sort_on.unwrap_or_default(); + let show_all = pagination_params.all.unwrap_or_default(); + let limit = pagination_params.count.unwrap_or(10); + let offset = (pagination_params.page.unwrap_or(1) - 1) * limit; + #[rustfmt::skip] let base_query = match (sort_on, sort_by) { (SortOn::LastModifiedAt, SortBy::Desc) => base_query.order(experiment_groups::last_modified_at.desc()), @@ -392,22 +457,68 @@ fn list_experiment_groups_db( (SortOn::Name, SortBy::Desc) => base_query.order(experiment_groups::name.desc()), (SortOn::Name, SortBy::Asc) => base_query.order(experiment_groups::name.asc()), }; - if let Some(true) = pagination_params.all { + + let perform_in_memory_filter = !dimension_params.is_empty(); + + let paginated_response = if perform_in_memory_filter { + let all_experiments: Vec = base_query.load(conn)?; + + let dimensions_info = + fetch_dimensions_info_map(conn, &workspace_context.schema_name)?; + let original_req_keys = dimension_params.keys().collect::>(); + let dimension_params = + evaluate_local_cohorts_skip_unresolved(&dimensions_info, &dimension_params); + + let filter_fn = match filters.dimension_match_strategy.unwrap_or_default() { + DimensionMatchStrategy::Exact => ExperimentGroup::filter_exact_match, + DimensionMatchStrategy::Subset => ExperimentGroup::filter_by_eval, + }; + + let dimension_filtered_experiments = + filter_fn(all_experiments, &dimension_params); + + let filtered_experiments = ExperimentGroup::filter_by_dimension( + dimension_filtered_experiments, + &original_req_keys, + &dimensions_info, + ); + + if show_all { + PaginatedResponse::all(filtered_experiments) + } else { + let total_items = filtered_experiments.len(); + let start = offset as usize; + let end = min((offset + limit) as usize, total_items); + let data = filtered_experiments + .get(start..end) + .map(|slice| slice.to_vec()) + .unwrap_or_default(); + + PaginatedResponse { + total_pages: (total_items as f64 / limit as f64).ceil() as i64, + total_items: total_items as i64, + data, + } + } + } else if show_all { let result: ExperimentGroups = base_query.get_results::(conn)?; - return Ok(PaginatedResponse::all(result)); - } - let total_items = count_query.count().get_result(conn)?; - let limit = pagination_params.count.unwrap_or(10); - let offset = (pagination_params.page.unwrap_or(1) - 1) * limit; + PaginatedResponse::all(result) + } else { + let total_items = count_query.count().get_result(conn)?; + let limit = pagination_params.count.unwrap_or(10); + let offset = (pagination_params.page.unwrap_or(1) - 1) * limit; + + let query = base_query.limit(limit).offset(offset); + let data = query.load::(conn)?; + let total_pages = (total_items as f64 / limit as f64).ceil() as i64; + PaginatedResponse { + total_pages, + total_items, + data, + } + }; - let query = base_query.limit(limit).offset(offset); - let data = query.load::(conn)?; - let total_pages = (total_items as f64 / limit as f64).ceil() as i64; - Ok(PaginatedResponse { - total_pages, - total_items, - data, - }) + Ok(paginated_response) } #[authorized] diff --git a/crates/experimentation_platform/src/api/experiment_groups/helpers.rs b/crates/experimentation_platform/src/api/experiment_groups/helpers.rs index d6d8e08a1..81eb04336 100644 --- a/crates/experimentation_platform/src/api/experiment_groups/helpers.rs +++ b/crates/experimentation_platform/src/api/experiment_groups/helpers.rs @@ -1,6 +1,7 @@ use std::collections::HashSet; use actix_web::web::{Data, Json}; +use chrono::{DateTime, Utc}; use diesel::{ BoolExpressionMethods, ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper, }; @@ -11,7 +12,10 @@ use fred::{ use serde_json::Value; use service_utils::{ helpers::{generate_snowflake_id, get_from_env_or_default}, - redis::EXPERIMENT_GROUPS_LIST_KEY_SUFFIX, + redis::{ + EXPERIMENT_CONFIG_LAST_MODIFIED_KEY_SUFFIX, + EXPERIMENT_GROUPS_LAST_MODIFIED_KEY_SUFFIX, EXPERIMENT_GROUPS_LIST_KEY_SUFFIX, + }, service::types::{AppState, SchemaName, WorkspaceContext}, }; use superposition_macros::{bad_argument, unexpected_error}; @@ -27,7 +31,8 @@ use superposition_types::{ }, }, schema::{ - experiment_groups::dsl as experiment_groups, experiments::dsl as experiments, + event_log::dsl as event_log, experiment_groups::dsl as experiment_groups, + experiments::dsl as experiments, }, }, result as superposition, @@ -482,11 +487,55 @@ pub async fn put_experiment_groups_in_redis( unexpected_error!("Failed to serialize experiment groups for redis: {}", e) })?; + let last_modified: Option> = event_log::event_log + .filter(event_log::table_name.eq("experiment_groups")) + .select(diesel::dsl::max(event_log::timestamp)) + .schema_name(schema_name) + .first(conn)?; + let key = format!("{}{EXPERIMENT_GROUPS_LIST_KEY_SUFFIX}", **schema_name); + let last_modified_at_key = format!( + "{}{EXPERIMENT_GROUPS_LAST_MODIFIED_KEY_SUFFIX}", + **schema_name, + ); + let config_modified_at_key = format!( + "{}{EXPERIMENT_CONFIG_LAST_MODIFIED_KEY_SUFFIX}", + **schema_name, + ); + let last_modified = last_modified.map(|dt| dt.to_rfc2822()).unwrap_or_default(); let key_ttl: i64 = get_from_env_or_default("REDIS_KEY_TTL", 604800); let expiration = Some(Expiration::EX(key_ttl)); - pool.next_connected() - .set::<(), String, String>(key, serialized, expiration, None, false) + + pool.set::<(), String, String>( + config_modified_at_key, + last_modified.clone(), + expiration.clone(), + None, + false, + ) + .await + .map_err(|e| { + log::warn!( + "failed to set experiment config last_modified_key in redis: {}", + e + ); + unexpected_error!("failed to set experiment config last_modified_key in redis") + })?; + + pool.set::<(), String, String>( + last_modified_at_key, + last_modified, + expiration.clone(), + None, + false, + ) + .await + .map_err(|e| { + log::warn!("failed to set experiment last_modified_key in redis: {}", e); + unexpected_error!("failed to set experiment last_modified_key in redis") + })?; + + pool.set::<(), String, String>(key, serialized, expiration, None, false) .await .map_err(|e| { log::warn!("Failed to write experiment groups to redis: {}", e); diff --git a/crates/experimentation_platform/src/api/experiments/handlers.rs b/crates/experimentation_platform/src/api/experiments/handlers.rs index 04dc08dc1..2187da709 100644 --- a/crates/experimentation_platform/src/api/experiments/handlers.rs +++ b/crates/experimentation_platform/src/api/experiments/handlers.rs @@ -26,7 +26,7 @@ use service_utils::{ db::run_query, helpers::{ WebhookData, construct_request_headers, execute_webhook_call, - fetch_dimensions_info_map, generate_snowflake_id, request, + fetch_dimensions_info_map, generate_snowflake_id, is_not_modified, request, }, middlewares::auth_z::{Action as AuthZAction, AuthZ}, redis::{ @@ -41,7 +41,7 @@ use superposition_derives::{authorized, declare_resource}; use superposition_macros::{bad_argument, unexpected_error}; use superposition_types::{ Cac, Condition, Contextual, DBConnection, DimensionInfo, Exp, ListResponse, - Overrides, PaginatedResponse, Resource, SortBy, User, + Overridden, Overrides, PaginatedResponse, Resource, SortBy, User, api::{ DimensionMatchStrategy, context::{ @@ -53,8 +53,8 @@ use superposition_types::{ experiments::{ ApplicableVariantsQuery, ApplicableVariantsRequest, ConcludeExperimentRequest, ExperimentCreateRequest, ExperimentListFilters, - ExperimentResponse, ExperimentSortOn, ExperimentStateChangeRequest, - OverrideKeysUpdateRequest, RampRequest, + ExperimentListRequest, ExperimentResponse, ExperimentSortOn, + ExperimentStateChangeRequest, OverrideKeysUpdateRequest, RampRequest, }, webhook::Action, }, @@ -1019,11 +1019,15 @@ async fn get_applicable_variants_handler( } } +#[allow(clippy::too_many_arguments)] #[authorized] +#[routes] #[get("")] +#[post("/list")] async fn list_handler( workspace_context: WorkspaceContext, req: HttpRequest, + body: Option>, pagination_params: superposition_query::Query, filters: superposition_query::Query, dimension_params: DimensionQuery, @@ -1047,28 +1051,32 @@ async fn list_handler( ) .await?; - let last_modified = req - .headers() - .get("If-Modified-Since") - .and_then(|header_val| header_val.to_str().ok()) - .and_then(|header_str| { - DateTime::parse_from_rfc2822(header_str) - .map(|datetime| datetime.with_timezone(&Utc)) - .ok() - }); - - if max_event_timestamp.is_some() && max_event_timestamp < last_modified { + if is_not_modified(max_event_timestamp, &req) { return Ok(HttpResponse::NotModified().finish()); }; let show_all = pagination_params.all.unwrap_or_default(); + + let is_smithy = matches!(req.method(), &actix_web::http::Method::POST); + let dimension_params = if req.method() == actix_web::http::Method::GET { + dimension_params.into_inner() + } else { + body.and_then(|b| b.into_inner().context) + .map(Into::into) + .unwrap_or_default() + }; + let read_from_redis = show_all && filters .status .clone() .is_some_and(|v| *v == ExperimentStatusType::active_list()) && dimension_params.is_empty(); + + let mut response = HttpResponse::Ok(); + AppHeader::add_last_modified(max_event_timestamp, is_smithy, &mut response); + if read_from_redis { - let response = read_through_cache::>( + read_through_cache::>( format!( "{}{EXPERIMENTS_LIST_KEY_SUFFIX}", *workspace_context.schema_name @@ -1078,18 +1086,17 @@ async fn list_handler( &state.db_pool, |conn| { list_experiments_db( - pagination_params.clone(), - filters.clone(), - dimension_params.clone(), + pagination_params, + filters, + dimension_params, conn, &workspace_context, ) }, ) - .await?; - Ok(HttpResponse::Ok().json(response)) + .await } else { - let paginated_response = run_query(&state.db_pool, |conn| { + run_query(&state.db_pool, |conn| { list_experiments_db( pagination_params, filters, @@ -1097,20 +1104,18 @@ async fn list_handler( conn, &workspace_context, ) - })?; - Ok(HttpResponse::Ok().json(paginated_response)) + }) } + .map(|r| response.json(r)) } fn list_experiments_db( pagination_params: superposition_query::Query, filters: superposition_query::Query, - dimension_params: DimensionQuery, + dimension_params: QueryMap, conn: &mut DBConnection, workspace_context: &WorkspaceContext, ) -> superposition::DieselResult> { - let dimension_params = dimension_params.into_inner(); - let query_builder = |filters: &ExperimentListFilters| { let mut builder = experiments::experiments .schema_name(&workspace_context.schema_name) @@ -1173,10 +1178,42 @@ fn list_experiments_db( let offset = (pagination_params.page.unwrap_or(1) - 1) * limit; let perform_in_memory_filter = !dimension_params.is_empty() - || filters.global_experiments_only.unwrap_or_default(); + || filters.global_experiments_only.unwrap_or_default() + || filters.prefix.is_some(); let paginated_response = if perform_in_memory_filter { - let all_experiments: Vec = base_query.load(conn)?; + let mut all_experiments: Vec = base_query.load(conn)?; + + if let Some(prefix) = filters.prefix.filter(|p| !p.is_empty()) { + let prefix_list = HashSet::from_iter(prefix.0); + all_experiments = all_experiments + .into_iter() + .filter_map(|experiment| { + let variants: Vec<_> = experiment + .variants + .into_iter() + .filter_map(|mut variant| { + Variant::filter_keys_by_prefix(&variant, &prefix_list) + .map(|filtered_overrides_map| { + variant.overrides = filtered_overrides_map; + variant + }) + .ok() + }) + .collect(); + + if !variants.is_empty() { + Some(Experiment { + variants: Variants::new(variants), + ..experiment + }) + } else { + None // Skip this experiment + } + }) + .collect() + } + let filtered_experiments = if filters.global_experiments_only.unwrap_or_default() { all_experiments diff --git a/crates/experimentation_platform/src/api/experiments/helpers.rs b/crates/experimentation_platform/src/api/experiments/helpers.rs index b4b5b819a..fd418077a 100644 --- a/crates/experimentation_platform/src/api/experiments/helpers.rs +++ b/crates/experimentation_platform/src/api/experiments/helpers.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use actix_http::header; use actix_web::web::Data; use cac_client::utils::json_to_sorted_string; -use chrono::Utc; +use chrono::{DateTime, Utc}; use diesel::{ BoolExpressionMethods, ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper, pg::PgConnection, @@ -16,7 +16,10 @@ use fred::{ use serde_json::{Map, Value}; use service_utils::{ helpers::get_from_env_or_default, - redis::EXPERIMENTS_LIST_KEY_SUFFIX, + redis::{ + EXPERIMENT_CONFIG_LAST_MODIFIED_KEY_SUFFIX, EXPERIMENTS_LAST_MODIFIED_KEY_SUFFIX, + EXPERIMENTS_LIST_KEY_SUFFIX, + }, service::types::{AppState, ExperimentationFlags, SchemaName, WorkspaceContext}, }; use superposition_macros::{bad_argument, unexpected_error}; @@ -41,7 +44,7 @@ use superposition_types::{ Variants, }, }, - schema::experiments::dsl as experiments, + schema::{event_log::dsl as event_log, experiments::dsl as experiments}, }, result as superposition, }; @@ -837,11 +840,53 @@ pub async fn put_experiments_in_redis( unexpected_error!("Failed to serialize experiments for redis: {}", e) })?; + let last_modified: Option> = event_log::event_log + .filter(event_log::table_name.eq("experiments")) + .select(diesel::dsl::max(event_log::timestamp)) + .schema_name(schema_name) + .first(conn)?; + let key = format!("{}{EXPERIMENTS_LIST_KEY_SUFFIX}", **schema_name); + let last_modified_at_key = + format!("{}{EXPERIMENTS_LAST_MODIFIED_KEY_SUFFIX}", **schema_name,); + let config_modified_at_key = format!( + "{}{EXPERIMENT_CONFIG_LAST_MODIFIED_KEY_SUFFIX}", + **schema_name, + ); + + let last_modified = last_modified.map(|dt| dt.to_rfc2822()).unwrap_or_default(); let key_ttl: i64 = get_from_env_or_default("REDIS_KEY_TTL", 604800); let expiration = Some(Expiration::EX(key_ttl)); - pool.next_connected() - .set::<(), String, String>(key, serialized, expiration, None, false) + + pool.set::<(), String, String>( + config_modified_at_key, + last_modified.clone(), + expiration.clone(), + None, + false, + ) + .await + .map_err(|e| { + log::warn!( + "failed to set experiment config last_modified_key in redis: {}", + e + ); + unexpected_error!("failed to set experiment config last_modified_key in redis") + })?; + + pool.set::<(), String, String>( + last_modified_at_key, + last_modified, + expiration.clone(), + None, + false, + ) + .await + .map_err(|e| { + log::warn!("failed to set experiment last_modified_key in redis: {}", e); + unexpected_error!("failed to set experiment last_modified_key in redis") + })?; + pool.set::<(), String, String>(key, serialized, expiration, None, false) .await .map_err(|e| { log::warn!("Failed to write experiments to redis: {}", e); diff --git a/crates/frontend/src/api.rs b/crates/frontend/src/api.rs index 05126219d..8257a7a21 100644 --- a/crates/frontend/src/api.rs +++ b/crates/frontend/src/api.rs @@ -1429,16 +1429,18 @@ pub mod experiment_groups { pub async fn list( filters: &ExpGroupFilters, pagination: &PaginationParams, + dimension_params: &DimensionQuery, workspace: &str, org_id: &str, ) -> Result, String> { let host = use_host_server(); let url = format!( - "{}/experiment-groups?{}&{}", + "{}/experiment-groups?{}&{}&{}", host, filters.to_query_param(), - pagination.to_query_param() + pagination.to_query_param(), + dimension_params.to_query_param() ); let response = request::<()>( url, diff --git a/crates/frontend/src/components/experiment_form.rs b/crates/frontend/src/components/experiment_form.rs index df5a5563c..419bc8afc 100644 --- a/crates/frontend/src/components/experiment_form.rs +++ b/crates/frontend/src/components/experiment_form.rs @@ -12,7 +12,7 @@ use superposition_types::{ experiments::{ExperimentResponse, OverrideKeysUpdateRequest}, functions::FunctionEnvironment, }, - custom_query::PaginationParams, + custom_query::{DimensionQuery, PaginationParams}, database::models::{ Metrics, cac::DefaultConfig, @@ -118,6 +118,7 @@ pub fn ExperimentForm( experiment_groups::list( &ExpGroupFilters::default(), &PaginationParams::all_entries(), + &DimensionQuery::default(), &workspace, &org, ) diff --git a/crates/frontend/src/pages/experiment_group_listing.rs b/crates/frontend/src/pages/experiment_group_listing.rs index e75fb6529..75c559b03 100644 --- a/crates/frontend/src/pages/experiment_group_listing.rs +++ b/crates/frontend/src/pages/experiment_group_listing.rs @@ -13,7 +13,7 @@ use superposition_types::{ dimension::DimensionResponse, experiment_groups::{ExpGroupFilters, SortOn}, }, - custom_query::{CustomQuery, PaginationParams, Query}, + custom_query::{CustomQuery, DimensionQuery, PaginationParams, Query, QueryMap}, database::models::experimentation::ExperimentGroup, }; use web_sys::MouseEvent; @@ -231,11 +231,12 @@ pub fn ExperimentGroupListing() -> impl IntoView { let org = use_context::>().unwrap(); let delete_inprogress_rws = RwSignal::new(false); - let (filters_rws, pagination_params_rws) = + let (filters_rws, pagination_params_rws, dimension_params_rws) = use_signal_from_query(move |query_string| { ( Query::::extract_non_empty(query_string).into_inner(), Query::::extract_non_empty(query_string).into_inner(), + DimensionQuery::::extract_non_empty(query_string), ) }); @@ -246,6 +247,7 @@ pub fn ExperimentGroupListing() -> impl IntoView { ( filters_rws.get(), pagination_params_rws.get(), + dimension_params_rws.get(), workspace.get().0, org.get().0, ) @@ -253,11 +255,16 @@ pub fn ExperimentGroupListing() -> impl IntoView { let experiment_groups_resource = create_blocking_resource( source, - |(filters, pagination, workspace, org_id)| async move { - let experiment_groups = - experiment_groups::list(&filters, &pagination, &workspace, &org_id) - .await - .unwrap_or_default(); + |(filters, pagination, dimension_params, workspace, org_id)| async move { + let experiment_groups = experiment_groups::list( + &filters, + &pagination, + &dimension_params, + &workspace, + &org_id, + ) + .await + .unwrap_or_default(); let dimensions = dimensions::list(&PaginationParams::all_entries(), &workspace, &org_id) .await @@ -323,7 +330,14 @@ pub fn ExperimentGroupListing() -> impl IntoView { number=total_items />
- + impl IntoView {
} - }} + }}
{move || { diff --git a/crates/frontend/src/pages/experiment_group_listing/filter.rs b/crates/frontend/src/pages/experiment_group_listing/filter.rs index a48b20436..04b029818 100644 --- a/crates/frontend/src/pages/experiment_group_listing/filter.rs +++ b/crates/frontend/src/pages/experiment_group_listing/filter.rs @@ -1,21 +1,38 @@ -use std::{fmt::Display, str::FromStr}; +use std::{fmt::Display, ops::Deref, str::FromStr}; use leptos::*; +use serde_json::Map; use superposition_types::{ - api::experiment_groups::ExpGroupFilters, - custom_query::{CommaSeparatedQParams, PaginationParams}, + api::{ + DimensionMatchStrategy, experiment_groups::ExpGroupFilters, + functions::FunctionEnvironment, + }, + custom_query::{ + CommaSeparatedQParams, CustomQuery, DimensionQuery, PaginationParams, QueryMap, + }, }; use web_sys::MouseEvent; -use crate::components::{ - badge::{GlassyPills, GrayPill, ListPills}, - button::{Button, ButtonStyle}, - drawer::{Drawer, DrawerBtn, close_drawer}, - form::label::Label, +use crate::{ + components::{ + badge::{GlassyPills, GrayPill, ListPills}, + button::{Button, ButtonStyle}, + condition_pills::Condition, + context_form::ContextForm, + drawer::{Drawer, DrawerBtn, close_drawer}, + form::label::Label, + input::Toggle, + }, + logic::Conditions, + pages::experiment_group_listing::CombinedResource, + providers::condition_collapse_provider::ConditionCollapseProvider, }; #[component] -pub(super) fn FilterSummary(filters_rws: RwSignal) -> impl IntoView { +pub(super) fn FilterSummary( + filters_rws: RwSignal, + dimension_params_rws: RwSignal>, +) -> impl IntoView { let force_open_rws = RwSignal::new(true); // let force_open_rws = RwSignal::new(scrolled_to_top.get_untracked()); @@ -36,7 +53,8 @@ pub(super) fn FilterSummary(filters_rws: RwSignal) -> impl Into f.created_by.is_none() && f.name.is_none() && f.last_modified_by.is_none() && f.group_type.is_none() }); - !filters_empty + let dimension_params_empty = dimension_params_rws.with(|f| f.is_empty()); + !filters_empty || !dimension_params_empty }>
) -> impl Into if force_open_rws.get() { "max-h-[1000px]" } else { "max-h-0" }, ) }> + {move || { + if !dimension_params_rws.with(|d| d.is_empty()) { + let dimension_params = dimension_params_rws.get(); + let conditions = Conditions::from_iter( + dimension_params.clone().into_inner(), + ); + let condition_id = serde_json::to_string( + dimension_params.clone().into_inner().deref(), + ) + .unwrap_or_else(|_| "[]".to_string()); + view! { +
+
"Context"
+ + + +
+ +
+ "Exact match context" + +
+
+ } + .into_view() + } else { + ().into_view() + } + }} {move || { filters_rws .with(|f| f.name.clone()) @@ -150,8 +214,19 @@ pub(super) fn FilterSummary(filters_rws: RwSignal) -> impl Into pub(super) fn ExperimentGroupFilterWidget( pagination_params_rws: RwSignal, filters_rws: RwSignal, + dimension_params_rws: RwSignal>, + combined_resource: CombinedResource, ) -> impl IntoView { let filters_buffer_rws = RwSignal::new(filters_rws.get_untracked()); + let dimension_buffer_rws = RwSignal::new(dimension_params_rws.get_untracked()); + let context_rws = RwSignal::new(Conditions::from_iter( + dimension_params_rws.get_untracked().into_inner(), + )); + + let fn_environment = Memo::new(move |_| FunctionEnvironment { + context: context_rws.get().into(), + overrides: Map::new(), + }); view! {
-
+ {move || { + view! { + + } + }} + {move || { + view! { +
+ +
+ } + }}
-
+
-
+