This provides SDK to interact with context-aware-config
.
- Context Aware Config Client Integration
- Rust
- Haskell
The rust client have a client factory that helps you work with multiple clients connected to different tenants
Create a client in the factory. You can chose to use the result to check for errors faced by the Client Factory while creating your client, it is not mandatory to consume the Ok
value.
pub async fn create_client(
tenant: String,
polling_interval: Duration,
hostname: String,
) -> Result<Arc<Client>, String>
Param | type | description | Example value |
---|---|---|---|
tenant |
String | specifies the tenants configs and contexts that will be loaded into the client at polling_interval from hostname |
mjos |
polling_interval |
Duration | specifies the time cac client waits before checking with the server for updates | Duration::from_secs(5) |
hostname |
String | The URL of the superposition server | https://superposition.example.com |
Get a client
pub async fn get_client(
tenant: String
) -> Result<Arc<Client>, String>
Param | type | description | Example value |
---|---|---|---|
tenant |
String | specifies the tenant used during create_client |
mjos |
Below is the rust implementation to instantiate CAC client using the client factory.
use cac_client as cc;
let tenants: Vec<String> = ["dev", "test"];
//You can create a clientFactory
for tenant in tenants {
cc::CLIENT_FACTORY
.create_client(
tenant.to_string(),
update_cac_periodically,//flag for if you want to update cac config periodically
polling_interval,//polling interval in secs, default is 60
cac_hostname.to_string(),// superposition service host
)
.await
.expect(format!("{}: Failed to acquire cac_client", tenant).as_str());
};
//You can extract an individual tenant's client from clientFactory
let tenant = "dev".to_owned();
let cac_client = cc::CLIENT_FACTORY.get_client(tenant.clone()).map_err(|e| {
log::error!("{}: {}", tenant.clone(), e);
ErrorType::IgnoreError(format!("{}: Failed to get cac client", tenant))
})?;
After calling get_client
method of Client Factory, you can do the following with the Client
returned.
the CAC client polls for updates from the superposition service and loads any changes done on the server. This means that configs changed in superposition are reflected on the client in the duration of polling_interval
. run_polling_updates()
should be run in a separate thread, as it does not terminate.
pub async fn run_polling_updates()
Get the full config definition of your tenants configuration from superposition. Config
has the following information:
pub struct Config {
contexts: Vec<Context>,
overrides: Map<String, Value>,
default_configs: Map<String, Value>,
}
pub fn get_full_config_state_with_filter(query_data: Option<Map<String, Value>>) -> Result<Config, String>
CAC client lets you get the last modified time of your configs, in case you want to log it, etc.
pub fn get_last_modified() -> Result<DateTime<Utc>, String>
Given a context, get overrides for a specific set of keys, if provided. If None is provided for filter_keys
, all configs are returned.
pub fn get_resolved_config(context: Map<String, Value>, filter_keys: Option<Vec<String>>) -> Result<Map<String, Value>, String>
Param | type | description | Example value |
---|---|---|---|
context |
Map<String, Value> | The context under which you want to resolve configs | {"os": "android", "merchant": "juspay"} |
filter_keys |
Option<Vec> | The keys for which you want the values. If empty, all configuration keys are returned | Some([payment, network, color]) |
The default config for a specific set of keys, if provided. If None is provided for filter_keys
, all configs are returned.
pub fn get_default_config(filter_keys: Option<Vec<String>>) -> Result<Map<String, Value>, String>
Param | type | description | Example value |
---|---|---|---|
filter_keys |
Option<Vec> | The keys for which you want the values. If None, all configuration keys are returned | Some([payment, network, color]) |
Add the following to your inputs
crane.url = "github:ipetkov/crane/54b63c8eae4c50172cb50b612946ff1d2bc1c75c";
crane.inputs.nixpkgs.follows = "common/nixpkgs";
superposition = {
url = "github:juspay/superposition";
inputs.nixpkgs.follows = "common/nixpkgs";
inputs.crane.follows = "crane";
};
then, add the following to your imports section in outputs:
imports = [
......
inputs.superposition.haskellFlakeProjectModules.output
]
then add the libraries to your project.cabal file:
extra-libraries:
cac_client
experimentation_client
Create a new client in the Client Factory
createCacClient:: Tenant -> Interval -> Hostname -> IO (Either Error ())
Param | type | description | Example value |
---|---|---|---|
Tenant |
String | specifies the tenants configs and contexts that will be loaded into the client at Interval from Hostname |
mjos |
Interval |
Duration | specifies the time cac client waits before checking with the server for updates, in seconds | 10 |
Hostname |
String | The URL of the superposition server | https://superposition.example.com |
Create a new client in the Client Factory
getCacClient :: Tenant -> IO (Either Error (ForeignPtr CacClient))
Param | type | description | Example value |
---|---|---|---|
Tenant |
String | specifies the tenants configs and contexts that will be loaded into the client at Interval from Hostname |
mjos |
the CAC client polls for updates from the superposition service and loads any changes done on the server. This means that configs changed in superposition are reflected on the client in the duration of Interval
. cacStartPolling
should be run in a separate thread, as it does not terminate.
cacStartPolling :: Tenant -> IO ()
Param | type | description | Example value |
---|---|---|---|
Tenant |
String | specifies the tenants configs and contexts that will be loaded into the client at Interval from Hostname |
mjos |
Get the full config definition of your tenants configuration from superposition. Config
has the following information:
{
contexts: [Context],
overrides: Map String Value,
default_configs: Map String Value,
}
getFullConfigStateWithFilter :: ForeignPtr CacClient -> Maybe String -> Maybe [String] -> IO (Either Error Value)
Param | type | description | Example value |
---|---|---|---|
context |
Maybe (String) | Specifies the context for which you want the configurations, If empty, all contexts are returned | Just {"os": "android", "merchant": "juspay"} |
prefix |
Maybe([String]) | The keys for which you want the values. If empty, all configuration keys are returned | Just ([payment, network, color]) |
CAC client lets you get the last modified time of your configs, in case you want to log it, etc.
getCacLastModified :: ForeignPtr CacClient -> IO (Either Error String)
Given a context, get overrides for a specific set of keys, if provided. If Nothing is provided for filter_keys
, all configs are returned.
getResolvedConfig :: ForeignPtr CacClient -> String -> Maybe [String] -> IO (Either Error Value)
Param | type | description | Example value |
---|---|---|---|
context |
String | The context under which you want to resolve configs | {"os": "android", "merchant": "juspay"} |
filter_keys |
Maybe([String]) | The keys for which you want the values. If empty, all configuration keys are returned | Just ([payment, network, color]) |
The default config for a specific set of keys, if provided. If Nothing is provided for filter_keys
, all configs are returned.
getDefaultConfig :: ForeignPtr CacClient -> Maybe [String] -> IO (Either Error Value)
Param | type | description | Example value |
---|---|---|---|
filter_keys |
Maybe([String]) | The keys for which you want the values. If Nothing, all configuration keys are returned | Just ([payment, network, color]) |
{-# LANGUAGE LambdaCase #-}
module Main (main) where
import Client (getResolvedConfig, createCacClient, getCacClient,
getFullConfigStateWithFilter, getCacLastModified, cacStartPolling, getDefaultConfig)
import Control.Concurrent
import Prelude
main :: IO ()
main = do
createCacClient "dev" 10 "http://localhost:8080" >>= \case
Left err -> putStrLn err
Right _ -> pure ()
threadId <- forkOS (cacStartPolling "dev")
print threadId
getCacClient "dev" >>= \case
Left err -> putStrLn err
Right client -> do
config <- getFullConfigStateWithFilter client Nothing Nothing
lastModified <- getCacLastModified client
overrides <- getResolvedConfig client "{\"country\": \"India\"}" $ Just ["country_image_url", "hyperpay_version"]
defaults <- getDefaultConfig client $ Just ["country_image_url", "hyperpay_version"]
filteredConfig <- getFullConfigStateWithFilter client (Just "{\"os\": \"android\"}") $ Just ["hyperpay"]
print config
print lastModified
print overrides
print defaults
print filteredConfig
threadDelay 1000000000
pure ()