Skip to content

Commit

Permalink
feat: adding lua cac and experimentation client wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
namitgoel committed Aug 2, 2024
1 parent 710c69d commit ed6bc42
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 0 deletions.
33 changes: 33 additions & 0 deletions clients/lua/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Set directory path that contains superposition object files in <span style="color: red" > SUPERPOSITION_LIB_PATH </span> env variable;

## [<u> CAC Client </u>](./cacclient/CacClient.lua)

1. This exports a class that exposes functions that internally call rust functions.
2. For Different platform it read different superposition object files.
* <span style="color: #808080" >For Mac </span> -> libcac_client.dylib
* <span style="color: #357EC7" >For Windows </span> -> libcac_client.so
* <span style="color: orange" >For Linux </span> -> libcac_client.dll
3. This run CAC CLient in two thread one is main thread another is worker thread.
4. Polling updates for config are done on different thread. ([ref](./cacclient/CacClient.lua#78)).


## [<u> Experimentation Client </u>](./expclient/ExperimentationClient.lua)

1. This exports a class that exposes functions that internally call rust functions.
2. For Different platform it read different superposition object files.
* <span style="color: #808080" >For Mac </span> -> libexperimentation_client.dylib
* <span style="color: #357EC7" >For Windows </span> -> libexperimentation_client.so
* <span style="color: orange" >For Linux </span> -> libexperimentation_client.dll
3. This run Experimentation CLient in two thread one is main thread another is worker thread.
4. Polling updates for experiments are done on different thread. ([ref](./expclient/ExperimentationClient.lua#80)).


## [<u> Test </u>](./main.lua)

1. To test this sample project follow below steps.
* Run superposition client.
* Run <u> **luajit main.lua** </u>.
2. By Default this sample code uses [dev](./main.lua#4) tenant.
3. By Default this sample code assumes superposition is running on [8080](./main.lua#6) port.
3. By Default this sample code polls superposition every [1 second](./main.lua#L5) port.
4. This sample code creates both [CAC CLient](./main.lua#12) and [Experimentation Client](./main.lua#8) with above default values.
106 changes: 106 additions & 0 deletions clients/lua/cacclient/CacClient.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
local ffi = require("ffi")
local platform = string.lower(jit.os)

local lib_path = os.getenv("SUPERPOSITION_LIB_PATH")
if not lib_path then
error("Environment variable SUPERPOSITION_LIB_PATH is not set")
end

if platform == "osx" then
lib_path = lib_path .. "/libcac_client.dylib"
elseif platform == "linux" then
lib_path = lib_path .. "/libcac_client.so"
elseif platform == "windows" then
lib_path = lib_path .. "/libcac_client.dll"
else
error("Unsupported platform: " .. platform)
end


local lib_path = "/Users/namit.goel/Desktop/repos/namit_superposition/superposition/target/debug/libcac_client.dylib"

ffi.cdef[[
int cac_new_client(const char* tenant_name, int polling_frequency, const char* cac_host_name);
const char* cac_get_client(const char* tenant_name);
void cac_start_polling_update(const char* tenant_name);
void cac_free_client(const char* client_ptr);
const char* cac_last_error_message();
int cac_last_error_length();
const char* cac_get_config(const char* client_ptr, const char* filter_query, const char* filter_prefix);
void cac_free_string(const char* string);
const char* cac_get_last_modified(const char* client_ptr);
const char* cac_get_resolved_config(const char* client_ptr, const char* query, const char* filter_keys, const char* merge_strategy);
const char* cac_get_default_config(const char* client_ptr, const char* filter_keys);
]]

local rust_lib = ffi.load(lib_path)

local CacClient = {}
CacClient.__index = CacClient

function CacClient:new(tenant_name, polling_frequency, cac_host_name)
assert(tenant_name and #tenant_name > 0, "tenantName cannot be null or empty")
assert(cac_host_name and #cac_host_name > 0, "cacHostName cannot be null or empty")

local self = setmetatable({}, CacClient)
self.tenant = tenant_name
self.polling_frequency = polling_frequency
self.cac_host_name = cac_host_name
return self
end

function CacClient:get_cac_last_error_message()
local error_message = rust_lib.cac_last_error_message()
if error_message == nil then
return "No Error"
end
return ffi.string(error_message)
end

function CacClient:get_cac_last_error_length()
return rust_lib.cac_last_error_length()
end

function CacClient:get_cac_client()
return ffi.string(rust_lib.cac_get_client(self.tenant))
end

function CacClient:create_new_cac_client()
local resp = rust_lib.cac_new_client(self.tenant, self.polling_frequency, self.cac_host_name)
if resp == 1 then
local error_message = self:get_cac_last_error_message()
print("Some Error Occur while creating new client ", error_message)
end
return resp
end

function CacClient:start_cac_polling_update()
rust_lib.cac_start_polling_update(self.tenant)
end

function CacClient:get_cac_config(filter_query, filter_prefix)
local client_ptr = self:get_cac_client()
return ffi.string(rust_lib.cac_get_config(client_ptr, filter_query, filter_prefix))
end

function CacClient:free_cac_client(client_ptr)
rust_lib.cac_free_client(client_ptr)
end

function CacClient:free_cac_string(string)
rust_lib.cac_free_string(string)
end

function CacClient:get_last_modified()
return ffi.string(rust_lib.cac_get_last_modified(self:get_cac_client()))
end

function CacClient:get_resolved_config(query, filter_keys, merge_strategy)
return ffi.string(rust_lib.cac_get_resolved_config(self:get_cac_client(), query, filter_keys, merge_strategy))
end

function CacClient:get_default_config(filter_keys)
return ffi.string(rust_lib.cac_get_default_config(self:get_cac_client(), filter_keys))
end

return CacClient
107 changes: 107 additions & 0 deletions clients/lua/expclient/ExperimentationClient.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
local ffi = require("ffi")
local platform = string.lower(jit.os)

local lib_path = os.getenv("SUPERPOSITION_LIB_PATH")
if not lib_path then
error("Environment variable SUPERPOSITION_LIB_PATH is not set")
end

if platform == "osx" then
lib_path = lib_path .. "/libexperimentation_client.dylib"
elseif platform == "linux" then
lib_path = lib_path .. "/libexperimentation_client.so"
elseif platform == "windows" then
lib_path = lib_path .. "/libexperimentation_client.dll"
else
error("Unsupported platform: " .. platform)
end

ffi.cdef[[
int expt_new_client(const char* tenant_name, int polling_frequency, const char* cac_host_name);
void expt_start_polling_update(const char* tenant_name);
const char* expt_get_client(const char* tenant_name);
const char* expt_get_applicable_variant(const char* client_ptr, const char* context, int toss);
const char* expt_get_satisfied_experiments(const char* client_ptr, const char* context, const char* filter_prefix);
const char* expt_get_filtered_satisfied_experiments(const char* client_ptr, const char* context, const char* filter_prefix);
const char* expt_get_running_experiments(const char* client_ptr);
void expt_free_string(const char* string);
const char* expt_last_error_message();
int expt_last_error_length();
void expt_free_client(const char* client_ptr);
]]

local rust_lib = ffi.load(lib_path)

local ExperimentationClient = {}
ExperimentationClient.__index = ExperimentationClient

function ExperimentationClient:new(tenant_name, polling_frequency, cac_host_name)
assert(tenant_name and #tenant_name > 0, "tenantName cannot be null or empty")
assert(cac_host_name and #cac_host_name > 0, "cacHostName cannot be null or empty")

local self = setmetatable({}, ExperimentationClient)
self.tenant = tenant_name
self.polling_frequency = polling_frequency
self.cac_host_name = cac_host_name
return self
end

function ExperimentationClient:get_experimentation_last_error_message()
local error_message = rust_lib.expt_last_error_message();
if error_message == nil then
return "No Error"
end
return ffi.string(rust_lib.expt_last_error_message())
end

function ExperimentationClient:create_new_experimentation_client()
local resp_code = rust_lib.expt_new_client(self.tenant, self.polling_frequency, self.cac_host_name)
if resp_code == 1 then
local error_message = self:get_experimentation_last_error_message()
print("Some error occurred while creating new experimentation client:", error_message)
error("Client Creation Error")
end
return resp_code
end

function ExperimentationClient:get_experimentation_client()
return ffi.string(rust_lib.expt_get_client(self.tenant))
end

function ExperimentationClient:get_running_experiments()
return ffi.string(rust_lib.expt_get_running_experiments(self:get_experimentation_client()))
end

function ExperimentationClient:free_string(string)
rust_lib.expt_free_string(string)
end

function ExperimentationClient:start_experimentation_polling_update()
rust_lib.expt_start_polling_update(self.tenant)
end

function ExperimentationClient:get_experimentation_last_error_length()
return rust_lib.expt_last_error_length()
end

function ExperimentationClient:free_experimentation_client()
rust_lib.expt_free_client(self:get_experimentation_client())
end

function ExperimentationClient:get_filtered_satisfied_experiments(context, filter_prefix)
local resp = rust_lib.expt_get_filtered_satisfied_experiments(self:get_experimentation_client(), context, filter_prefix)
if resp == nil then
return ""
end
return ffi.string(resp)
end

function ExperimentationClient:get_applicable_variant(context, toss)
return ffi.string(rust_lib.expt_get_applicable_variant(self:get_experimentation_client(), context, toss))
end

function ExperimentationClient:get_satisfied_experiments(context, filter_prefix)
return ffi.string(rust_lib.expt_get_satisfied_experiments(self:get_experimentation_client(), context, filter_prefix))
end

return ExperimentationClient
14 changes: 14 additions & 0 deletions clients/lua/main.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
local CacClient = require("cacclient.CacClient")
local ExperimentationClient = require("expclient.ExperimentationClient")

local tenant_name = "dev"
local polling_frequency = 1
local cac_host_name = "http://localhost:8080"

local exp_client = ExperimentationClient:new(tenant_name, polling_frequency, cac_host_name)
local response = exp_client:create_new_experimentation_client()
print(exp_client:get_satisfied_experiments("{}",""))

local cac_client = CacClient:new(tenant_name, polling_frequency, cac_host_name)
local response = cac_client:create_new_cac_client()
print(cac_client:get_last_modified())

0 comments on commit ed6bc42

Please sign in to comment.