Skip to content

Commit

Permalink
fix: Use jsonlogc's partial_apply for config and experiment filtering (
Browse files Browse the repository at this point in the history
  • Loading branch information
ayushjain17 authored Jun 20, 2024
1 parent 45bb30f commit 97bf39b
Show file tree
Hide file tree
Showing 15 changed files with 323 additions and 247 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 16 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ members = [
"crates/frontend",
"crates/caclang",
"crates/superposition",
"crates/superposition_types"
]
"crates/superposition_types",
]

[[workspace.metadata.leptos]]
name = "cac"
Expand All @@ -29,20 +29,27 @@ assets-dir = "crates/frontend/assets"
dotenv = "0.15.0"
actix = "0.13.0"
actix-web = "4.5.0"
diesel = { version = "2.1.0", features = ["postgres", "r2d2", "serde_json", "chrono", "uuid", "postgres_backend"] }
diesel = { version = "2.1.0", features = [
"postgres",
"r2d2",
"serde_json",
"chrono",
"uuid",
"postgres_backend",
] }
env_logger = "0.8"
log = { version="0.4.20", features = ["kv_unstable_serde"] }
serde = {version = "^1", features = ["derive"]}
serde_json = {version = "1.0"}
log = { version = "0.4.20", features = ["kv_unstable_serde"] }
serde = { version = "^1", features = ["derive"] }
serde_json = { version = "1.0" }
derive_more = "^0.99"
base64 = "0.21.2"
urlencoding = "~2.1.2"
regex = "1.9.1"
chrono = { version = "0.4.26", features = ["serde"] }
uuid = {version = "1.3.4", features = ["v4", "serde"]}
reqwest = { version = "0.11.18", features = ["json"]}
uuid = { version = "1.3.4", features = ["v4", "serde"] }
reqwest = { version = "0.11.18", features = ["json"] }
jsonschema = "~0.17"
jsonlogic = { git = "https://github.com/juspay/jsonlogic_rs.git", version = "0.5.2" }
jsonlogic = { git = "https://github.com/juspay/jsonlogic_rs.git", version = "0.5.3" }
rs-snowflake = "0.6.0"
rusoto_kms = "0.48.0"
rusoto_signature = "0.48.0"
Expand Down
23 changes: 13 additions & 10 deletions clients/haskell/hs-cac-client/src/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ module Client

import Data.Aeson
import Data.Functor (($>))
import Data.List (intercalate)
import Foreign.C.String (CString, newCAString, peekCAString)
import Foreign.C.Types (CInt (CInt), CULong (..))
import Foreign.ForeignPtr
import Foreign.Marshal.Alloc (free)
import Foreign.Marshal.Array (withArrayLen)
import Data.List (intercalate)
import Foreign.Ptr
import Prelude

Expand Down Expand Up @@ -50,7 +50,7 @@ foreign import ccall unsafe "cac_get_last_modified"
c_get_last_modified_time :: Ptr CacClient -> IO CString

foreign import ccall unsafe "cac_get_config"
c_get_config :: Ptr CacClient -> CString -> IO CString
c_get_config :: Ptr CacClient -> CString -> CString -> IO CString

foreign import ccall unsafe "cac_get_resolved_config"
c_cac_get_resolved_config :: Ptr CacClient -> CString -> CString -> CString -> IO CString
Expand Down Expand Up @@ -100,12 +100,15 @@ getCacClient tenant = do
then Left <$> getError
else Right <$> newForeignPtr c_free_cac_client cacClient

getFullConfigStateWithFilter :: ForeignPtr CacClient -> Maybe String -> IO (Either Error Value)
getFullConfigStateWithFilter client mbFilters = do
getFullConfigStateWithFilter :: ForeignPtr CacClient -> Maybe String -> Maybe String -> IO (Either Error Value)
getFullConfigStateWithFilter client mbFilters mbPrefix = do
cFilters <- case mbFilters of
Just filters -> newCAString filters
Nothing -> return nullPtr
config <- withForeignPtr client (`c_get_config` cFilters)
Nothing -> return nullPtr
cPrefix <- case mbPrefix of
Just prefix -> newCAString prefix
Nothing -> return nullPtr
config <- withForeignPtr client $ \client -> c_get_config client cFilters cPrefix
_ <- cleanup [cFilters]
if config == nullPtr
then Left <$> getError
Expand All @@ -127,8 +130,8 @@ getResolvedConfigWithStrategy client context mbKeys mergeStrat = do
cContext <- newCAString context
cMergeStrat <- newCAString (show mergeStrat)
cStrKeys <- case mbKeys of
Just keys -> newCAString (intercalate "|" keys)
Nothing -> return nullPtr
Just keys -> newCAString (intercalate "|" keys)
Nothing -> return nullPtr
overrides <- withForeignPtr client $ \client -> c_cac_get_resolved_config client cContext cStrKeys cMergeStrat
_ <- cleanup [cContext, cStrKeys]
if overrides == nullPtr
Expand All @@ -140,8 +143,8 @@ getResolvedConfigWithStrategy client context mbKeys mergeStrat = do
getDefaultConfig :: ForeignPtr CacClient -> Maybe [String] -> IO (Either Error Value)
getDefaultConfig client mbKeys = do
cStrKeys <- case mbKeys of
Just keys -> newCAString (intercalate "|" keys)
Nothing -> return nullPtr
Just keys -> newCAString (intercalate "|" keys)
Nothing -> return nullPtr
overrides <- withForeignPtr client $ \client -> c_cac_get_default_config client cStrKeys
_ <- cleanup [cStrKeys]
if overrides == nullPtr
Expand Down
11 changes: 7 additions & 4 deletions clients/haskell/hs-cac-client/src/Main.hs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
{-# LANGUAGE LambdaCase #-}
module Main (main) where

import Client (getResolvedConfig, createCacClient, getCacClient,
getFullConfigStateWithFilter, getCacLastModified, cacStartPolling, getDefaultConfig)
import Client (cacStartPolling, createCacClient,
getCacClient, getCacLastModified,
getDefaultConfig,
getFullConfigStateWithFilter,
getResolvedConfig)
import Control.Concurrent
import Prelude

Expand All @@ -16,11 +19,11 @@ main = do
getCacClient "dev" >>= \case
Left err -> putStrLn err
Right client -> do
config <- getFullConfigStateWithFilter client Nothing
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 "{\"prefix\": \"hyperpay\", \"os\": \"android\"}"
filteredConfig <- getFullConfigStateWithFilter client (Just "{\"os\": \"android\"}") (Just "hyperpay")
print config
print lastModified
print overrides
Expand Down
31 changes: 27 additions & 4 deletions clients/haskell/hs-exp-client/src/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module Client
, createExpClient
, getApplicableVariants
, getSatisfiedExperiments
, getFilteredSatisfiedExperiments
, getRunningExperiments
) where

Expand Down Expand Up @@ -53,7 +54,10 @@ foreign import ccall unsafe "expt_get_applicable_variant"
c_get_applicable_variants :: Ptr ExpClient -> CString -> CShort -> IO CString

foreign import ccall unsafe "expt_get_satisfied_experiments"
c_get_satisfied_experiments :: Ptr ExpClient -> CString -> IO CString
c_get_satisfied_experiments :: Ptr ExpClient -> CString -> CString -> IO CString

foreign import ccall unsafe "expt_get_filtered_satisfied_experiments"
c_get_filtered_satisfied_experiments :: Ptr ExpClient -> CString -> CString -> IO CString

foreign import ccall unsafe "expt_get_running_experiments"
c_get_running_experiments :: Ptr ExpClient -> IO CString
Expand Down Expand Up @@ -107,17 +111,36 @@ getApplicableVariants client query toss = do
-- Error s -> Left s
-- Success vec -> Right vec

getSatisfiedExperiments :: ForeignPtr ExpClient -> String -> IO (Either Error Value)
getSatisfiedExperiments client query = do
getSatisfiedExperiments :: ForeignPtr ExpClient -> String -> Maybe String -> IO (Either Error Value)
getSatisfiedExperiments client query mbPrefix = do
context <- newCAString query
experiments <- withForeignPtr client (`c_get_satisfied_experiments` context)
prefix <- case mbPrefix of
Just prefix -> newCAString prefix
Nothing -> return nullPtr
experiments <- withForeignPtr client $ \client -> c_get_satisfied_experiments client context prefix
_ <- cleanup [context]
if experiments == nullPtr
then Left <$> getError
else do
fptrExperiments <- newForeignPtr c_free_string experiments
Right . toJSON <$> withForeignPtr fptrExperiments peekCAString

getFilteredSatisfiedExperiments :: ForeignPtr ExpClient -> Maybe String -> Maybe String -> IO (Either Error Value)
getFilteredSatisfiedExperiments client mbFilters mbPrefix = do
filters <- case mbFilters of
Just filters' -> newCAString filters'
Nothing -> return nullPtr
prefix <- case mbPrefix of
Just prefix' -> newCAString prefix'
Nothing -> return nullPtr
experiments <- withForeignPtr client $ \client -> c_get_filtered_satisfied_experiments client filters prefix
_ <- cleanup [filters]
if experiments == nullPtr
then Left <$> getError
else do
fptrExperiments <- newForeignPtr c_free_string experiments
Right . toJSON <$> withForeignPtr fptrExperiments peekCAString

getRunningExperiments :: ForeignPtr ExpClient -> IO (Either Error Value)
getRunningExperiments client = do
experiments <- withForeignPtr client c_get_running_experiments
Expand Down
6 changes: 5 additions & 1 deletion clients/haskell/hs-exp-client/src/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Main (main) where

import Client (createExpClient, expStartPolling,
getApplicableVariants, getExpClient,
getFilteredSatisfiedExperiments,
getRunningExperiments,
getSatisfiedExperiments)
import Control.Concurrent
Expand All @@ -22,12 +23,15 @@ main = do
where
loop client = do
runningExperiments <- getRunningExperiments client
satisfiedExperiments <- getSatisfiedExperiments client "{\"os\": \"android\", \"client\": \"1mg\"}"
satisfiedExperiments <- getSatisfiedExperiments client "{\"os\": \"android\", \"client\": \"1mg\"}" Nothing
filteredExperiments <- getFilteredSatisfiedExperiments client (Just "{\"os\": \"android\"}") (Just "hyperpay")
variants <- getApplicableVariants client "{\"os\": \"android\", \"client\": \"1mg\"}" 9
print "Running experiments"
print runningExperiments
print "experiments that satisfy context"
print satisfiedExperiments
print "experiments after filtering"
print filteredExperiments
print "variant ID applied"
print variants
-- threadDelay 10000000
Expand Down
51 changes: 28 additions & 23 deletions crates/cac_client/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,38 +176,43 @@ pub extern "C" fn cac_get_last_modified(client: *mut Arc<Client>) -> *const c_ch
#[no_mangle]
pub extern "C" fn cac_get_config(
client: *mut Arc<Client>,
query: *const c_char,
filter_query: *const c_char,
filter_prefix: *const c_char,
) -> *const c_char {
let filter = if query.is_null() {
None
} else {
let filter_string = match cstring_to_rstring(query) {
Ok(s) => s,
Err(err) => {
update_last_error(err);
return std::ptr::null();
}
};
let filters: Map<String, Value> =
match serde_json::from_str::<Map<String, Value>>(filter_string.as_str()) {
Ok(json) => json,
Err(err) => {
update_last_error(err.to_string());
return std::ptr::null();
}
};
Some(filters)
};

null_check!(
client,
"an invalid null pointer client is being used, please call get_client()",
return std::ptr::null()
);

let filters = if filter_query.is_null() {
None
} else {
let filter_string =
unwrap_safe!(cstring_to_rstring(filter_query), return std::ptr::null());
let filters: Map<String, Value> = unwrap_safe!(
serde_json::from_str::<Map<String, Value>>(filter_string.as_str()),
return std::ptr::null()
);

Some(filters).filter(|filters| !filters.is_empty())
};

let prefix_list = if filter_prefix.is_null() {
None
} else {
let filter_string =
unwrap_safe!(cstring_to_rstring(filter_prefix), return std::ptr::null());
let prefix_list: Vec<String> =
filter_string.split(',').map(String::from).collect();

Some(prefix_list).filter(|list| !list.is_empty())
};

unwrap_safe!(
unsafe {
(*client)
.get_full_config_state_with_filter(filter)
.get_full_config_state_with_filter(filters, prefix_list)
.map(|config| {
rstring_to_cstring(serde_json::to_value(config).unwrap().to_string())
.into_raw()
Expand Down
Loading

0 comments on commit 97bf39b

Please sign in to comment.