Skip to main content

Context Aware Config Client Integration

This provides SDK to interact with context-aware-config.


Rust

The rust client have a client factory that helps you work with multiple clients connected to different tenants

Client Factory Methods Reference

Create Client

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.

Function definition
pub async fn create_client(
tenant: String,
polling_interval: Duration,
hostname: String,
) -> Result<Arc<Client>, String>
Params
ParamtypedescriptionExample value
tenantStringspecifies the tenants configs and contexts that will be loaded into the client at polling_interval from hostnamemjos
polling_intervalDurationspecifies the time cac client waits before checking with the server for updatesDuration::from_secs(5)
hostnameStringThe URL of the superposition serverhttps://superposition.example.com

Get Client

Get a client

Function definition
pub async fn get_client(
tenant: String
) -> Result<Arc<Client>, String>
Params
ParamtypedescriptionExample value
tenantStringspecifies the tenant used during create_clientmjos

Example Implementation

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))
})?;

CAC Client Methods Reference

After calling get_client method of Client Factory, you can do the following with the Client returned.

Run polling for updates from Superposition Service

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.

Function definition
pub async fn run_polling_updates()

Get Config

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>,
}
Funtion Definition
pub fn get_full_config_state_with_filter(query_data: Option<Map<String, Value>>) -> Result<Config, String>

Get the last modified Time

CAC client lets you get the last modified time of your configs, in case you want to log it, etc.

Function Definition
pub fn get_last_modified() -> Result<DateTime<Utc>, String>

Evaluate Context to derive configs

Given a context, get overrides for a specific set of keys, if provided. If None is provided for filter_keys, all configs are returned.

Function Definition
pub fn get_resolved_config(context: Map<String, Value>, filter_keys: Option<Vec<String>>) -> Result<Map<String, Value>, String>
Params
ParamtypedescriptionExample value
contextMap<String, Value>The context under which you want to resolve configs{"os": "android", "merchant": "juspay"}
filter_keysOption<Vec<String>>The keys for which you want the values. If empty, all configuration keys are returnedSome([payment, network, color])

Get Default Config

The default config for a specific set of keys, if provided. If None is provided for filter_keys, all configs are returned.

Function Definition
pub fn get_default_config(filter_keys: Option<Vec<String>>) -> Result<Map<String, Value>, String>
Param
ParamtypedescriptionExample value
filter_keysOption<Vec<String>>The keys for which you want the values. If None, all configuration keys are returnedSome([payment, network, color])

Haskell

Adding the clients to your project

Nix

Add the following to your inputs

superposition = {
url = "github:juspay/superposition";
inputs.nixpkgs.follows = "common/nixpkgs";
};

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

Haskell CAC client functions reference

Create a client

Create a new client in the Client Factory

Function Definition
createCacClient:: Tenant -> Interval -> Hostname -> IO (Either Error ())
Param
ParamtypedescriptionExample value
TenantStringspecifies the tenants configs and contexts that will be loaded into the client at Interval from Hostnamemjos
IntervalDurationspecifies the time cac client waits before checking with the server for updates, in seconds10
HostnameStringThe URL of the superposition serverhttps://superposition.example.com

Get a client

Create a new client in the Client Factory

Function Definition
getCacClient :: Tenant -> IO (Either Error (ForeignPtr CacClient))
Param
ParamtypedescriptionExample value
TenantStringspecifies the tenants configs and contexts that will be loaded into the client at Interval from Hostnamemjos

Run polling for updates from Superposition Service

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.

Function definition
cacStartPolling :: Tenant -> IO ()
Param
ParamtypedescriptionExample value
TenantStringspecifies the tenants configs and contexts that will be loaded into the client at Interval from Hostnamemjos

Get Config

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,
}
Funtion Definition
getFullConfigStateWithFilter :: ForeignPtr CacClient -> Maybe String -> Maybe [String] -> IO (Either Error Value)
Params
ParamtypedescriptionExample value
contextMaybe (String)Specifies the context for which you want the configurations, If empty, all contexts are returnedJust {"os": "android", "merchant": "juspay"}
prefixMaybe([String])The keys for which you want the values. If empty, all configuration keys are returnedJust ([payment, network, color])

Get the last modified Time

CAC client lets you get the last modified time of your configs, in case you want to log it, etc.

Function Definition
getCacLastModified :: ForeignPtr CacClient -> IO (Either Error String)

Evaluate Context to derive configs

Given a context, get overrides for a specific set of keys, if provided. If Nothing is provided for filter_keys, all configs are returned.

Function Definition
getResolvedConfig :: ForeignPtr CacClient -> String -> Maybe [String] -> IO (Either Error Value)
Params
ParamtypedescriptionExample value
contextStringThe context under which you want to resolve configs{"os": "android", "merchant": "juspay"}
filter_keysMaybe([String])The keys for which you want the values. If empty, all configuration keys are returnedJust ([payment, network, color])

Get Default Config

The default config for a specific set of keys, if provided. If Nothing is provided for filter_keys, all configs are returned.

Function Definition
getDefaultConfig :: ForeignPtr CacClient -> Maybe [String] -> IO (Either Error Value)
ParamtypedescriptionExample value
filter_keysMaybe([String])The keys for which you want the values. If Nothing, all configuration keys are returnedJust ([payment, network, color])

Sample Integration

{-# 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 ()