Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding lua cac and experimentation client wrapper #196

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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())