Skip to content

Commit ed6bc42

Browse files
committed
feat: adding lua cac and experimentation client wrapper
1 parent 710c69d commit ed6bc42

File tree

4 files changed

+260
-0
lines changed

4 files changed

+260
-0
lines changed

clients/lua/Readme.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
Set directory path that contains superposition object files in <span style="color: red" > SUPERPOSITION_LIB_PATH </span> env variable;
2+
3+
## [<u> CAC Client </u>](./cacclient/CacClient.lua)
4+
5+
1. This exports a class that exposes functions that internally call rust functions.
6+
2. For Different platform it read different superposition object files.
7+
* <span style="color: #808080" >For Mac </span> -> libcac_client.dylib
8+
* <span style="color: #357EC7" >For Windows </span> -> libcac_client.so
9+
* <span style="color: orange" >For Linux </span> -> libcac_client.dll
10+
3. This run CAC CLient in two thread one is main thread another is worker thread.
11+
4. Polling updates for config are done on different thread. ([ref](./cacclient/CacClient.lua#78)).
12+
13+
14+
## [<u> Experimentation Client </u>](./expclient/ExperimentationClient.lua)
15+
16+
1. This exports a class that exposes functions that internally call rust functions.
17+
2. For Different platform it read different superposition object files.
18+
* <span style="color: #808080" >For Mac </span> -> libexperimentation_client.dylib
19+
* <span style="color: #357EC7" >For Windows </span> -> libexperimentation_client.so
20+
* <span style="color: orange" >For Linux </span> -> libexperimentation_client.dll
21+
3. This run Experimentation CLient in two thread one is main thread another is worker thread.
22+
4. Polling updates for experiments are done on different thread. ([ref](./expclient/ExperimentationClient.lua#80)).
23+
24+
25+
## [<u> Test </u>](./main.lua)
26+
27+
1. To test this sample project follow below steps.
28+
* Run superposition client.
29+
* Run <u> **luajit main.lua** </u>.
30+
2. By Default this sample code uses [dev](./main.lua#4) tenant.
31+
3. By Default this sample code assumes superposition is running on [8080](./main.lua#6) port.
32+
3. By Default this sample code polls superposition every [1 second](./main.lua#L5) port.
33+
4. This sample code creates both [CAC CLient](./main.lua#12) and [Experimentation Client](./main.lua#8) with above default values.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
local ffi = require("ffi")
2+
local platform = string.lower(jit.os)
3+
4+
local lib_path = os.getenv("SUPERPOSITION_LIB_PATH")
5+
if not lib_path then
6+
error("Environment variable SUPERPOSITION_LIB_PATH is not set")
7+
end
8+
9+
if platform == "osx" then
10+
lib_path = lib_path .. "/libcac_client.dylib"
11+
elseif platform == "linux" then
12+
lib_path = lib_path .. "/libcac_client.so"
13+
elseif platform == "windows" then
14+
lib_path = lib_path .. "/libcac_client.dll"
15+
else
16+
error("Unsupported platform: " .. platform)
17+
end
18+
19+
20+
local lib_path = "/Users/namit.goel/Desktop/repos/namit_superposition/superposition/target/debug/libcac_client.dylib"
21+
22+
ffi.cdef[[
23+
int cac_new_client(const char* tenant_name, int polling_frequency, const char* cac_host_name);
24+
const char* cac_get_client(const char* tenant_name);
25+
void cac_start_polling_update(const char* tenant_name);
26+
void cac_free_client(const char* client_ptr);
27+
const char* cac_last_error_message();
28+
int cac_last_error_length();
29+
const char* cac_get_config(const char* client_ptr, const char* filter_query, const char* filter_prefix);
30+
void cac_free_string(const char* string);
31+
const char* cac_get_last_modified(const char* client_ptr);
32+
const char* cac_get_resolved_config(const char* client_ptr, const char* query, const char* filter_keys, const char* merge_strategy);
33+
const char* cac_get_default_config(const char* client_ptr, const char* filter_keys);
34+
]]
35+
36+
local rust_lib = ffi.load(lib_path)
37+
38+
local CacClient = {}
39+
CacClient.__index = CacClient
40+
41+
function CacClient:new(tenant_name, polling_frequency, cac_host_name)
42+
assert(tenant_name and #tenant_name > 0, "tenantName cannot be null or empty")
43+
assert(cac_host_name and #cac_host_name > 0, "cacHostName cannot be null or empty")
44+
45+
local self = setmetatable({}, CacClient)
46+
self.tenant = tenant_name
47+
self.polling_frequency = polling_frequency
48+
self.cac_host_name = cac_host_name
49+
return self
50+
end
51+
52+
function CacClient:get_cac_last_error_message()
53+
local error_message = rust_lib.cac_last_error_message()
54+
if error_message == nil then
55+
return "No Error"
56+
end
57+
return ffi.string(error_message)
58+
end
59+
60+
function CacClient:get_cac_last_error_length()
61+
return rust_lib.cac_last_error_length()
62+
end
63+
64+
function CacClient:get_cac_client()
65+
return ffi.string(rust_lib.cac_get_client(self.tenant))
66+
end
67+
68+
function CacClient:create_new_cac_client()
69+
local resp = rust_lib.cac_new_client(self.tenant, self.polling_frequency, self.cac_host_name)
70+
if resp == 1 then
71+
local error_message = self:get_cac_last_error_message()
72+
print("Some Error Occur while creating new client ", error_message)
73+
end
74+
return resp
75+
end
76+
77+
function CacClient:start_cac_polling_update()
78+
rust_lib.cac_start_polling_update(self.tenant)
79+
end
80+
81+
function CacClient:get_cac_config(filter_query, filter_prefix)
82+
local client_ptr = self:get_cac_client()
83+
return ffi.string(rust_lib.cac_get_config(client_ptr, filter_query, filter_prefix))
84+
end
85+
86+
function CacClient:free_cac_client(client_ptr)
87+
rust_lib.cac_free_client(client_ptr)
88+
end
89+
90+
function CacClient:free_cac_string(string)
91+
rust_lib.cac_free_string(string)
92+
end
93+
94+
function CacClient:get_last_modified()
95+
return ffi.string(rust_lib.cac_get_last_modified(self:get_cac_client()))
96+
end
97+
98+
function CacClient:get_resolved_config(query, filter_keys, merge_strategy)
99+
return ffi.string(rust_lib.cac_get_resolved_config(self:get_cac_client(), query, filter_keys, merge_strategy))
100+
end
101+
102+
function CacClient:get_default_config(filter_keys)
103+
return ffi.string(rust_lib.cac_get_default_config(self:get_cac_client(), filter_keys))
104+
end
105+
106+
return CacClient
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
local ffi = require("ffi")
2+
local platform = string.lower(jit.os)
3+
4+
local lib_path = os.getenv("SUPERPOSITION_LIB_PATH")
5+
if not lib_path then
6+
error("Environment variable SUPERPOSITION_LIB_PATH is not set")
7+
end
8+
9+
if platform == "osx" then
10+
lib_path = lib_path .. "/libexperimentation_client.dylib"
11+
elseif platform == "linux" then
12+
lib_path = lib_path .. "/libexperimentation_client.so"
13+
elseif platform == "windows" then
14+
lib_path = lib_path .. "/libexperimentation_client.dll"
15+
else
16+
error("Unsupported platform: " .. platform)
17+
end
18+
19+
ffi.cdef[[
20+
int expt_new_client(const char* tenant_name, int polling_frequency, const char* cac_host_name);
21+
void expt_start_polling_update(const char* tenant_name);
22+
const char* expt_get_client(const char* tenant_name);
23+
const char* expt_get_applicable_variant(const char* client_ptr, const char* context, int toss);
24+
const char* expt_get_satisfied_experiments(const char* client_ptr, const char* context, const char* filter_prefix);
25+
const char* expt_get_filtered_satisfied_experiments(const char* client_ptr, const char* context, const char* filter_prefix);
26+
const char* expt_get_running_experiments(const char* client_ptr);
27+
void expt_free_string(const char* string);
28+
const char* expt_last_error_message();
29+
int expt_last_error_length();
30+
void expt_free_client(const char* client_ptr);
31+
]]
32+
33+
local rust_lib = ffi.load(lib_path)
34+
35+
local ExperimentationClient = {}
36+
ExperimentationClient.__index = ExperimentationClient
37+
38+
function ExperimentationClient:new(tenant_name, polling_frequency, cac_host_name)
39+
assert(tenant_name and #tenant_name > 0, "tenantName cannot be null or empty")
40+
assert(cac_host_name and #cac_host_name > 0, "cacHostName cannot be null or empty")
41+
42+
local self = setmetatable({}, ExperimentationClient)
43+
self.tenant = tenant_name
44+
self.polling_frequency = polling_frequency
45+
self.cac_host_name = cac_host_name
46+
return self
47+
end
48+
49+
function ExperimentationClient:get_experimentation_last_error_message()
50+
local error_message = rust_lib.expt_last_error_message();
51+
if error_message == nil then
52+
return "No Error"
53+
end
54+
return ffi.string(rust_lib.expt_last_error_message())
55+
end
56+
57+
function ExperimentationClient:create_new_experimentation_client()
58+
local resp_code = rust_lib.expt_new_client(self.tenant, self.polling_frequency, self.cac_host_name)
59+
if resp_code == 1 then
60+
local error_message = self:get_experimentation_last_error_message()
61+
print("Some error occurred while creating new experimentation client:", error_message)
62+
error("Client Creation Error")
63+
end
64+
return resp_code
65+
end
66+
67+
function ExperimentationClient:get_experimentation_client()
68+
return ffi.string(rust_lib.expt_get_client(self.tenant))
69+
end
70+
71+
function ExperimentationClient:get_running_experiments()
72+
return ffi.string(rust_lib.expt_get_running_experiments(self:get_experimentation_client()))
73+
end
74+
75+
function ExperimentationClient:free_string(string)
76+
rust_lib.expt_free_string(string)
77+
end
78+
79+
function ExperimentationClient:start_experimentation_polling_update()
80+
rust_lib.expt_start_polling_update(self.tenant)
81+
end
82+
83+
function ExperimentationClient:get_experimentation_last_error_length()
84+
return rust_lib.expt_last_error_length()
85+
end
86+
87+
function ExperimentationClient:free_experimentation_client()
88+
rust_lib.expt_free_client(self:get_experimentation_client())
89+
end
90+
91+
function ExperimentationClient:get_filtered_satisfied_experiments(context, filter_prefix)
92+
local resp = rust_lib.expt_get_filtered_satisfied_experiments(self:get_experimentation_client(), context, filter_prefix)
93+
if resp == nil then
94+
return ""
95+
end
96+
return ffi.string(resp)
97+
end
98+
99+
function ExperimentationClient:get_applicable_variant(context, toss)
100+
return ffi.string(rust_lib.expt_get_applicable_variant(self:get_experimentation_client(), context, toss))
101+
end
102+
103+
function ExperimentationClient:get_satisfied_experiments(context, filter_prefix)
104+
return ffi.string(rust_lib.expt_get_satisfied_experiments(self:get_experimentation_client(), context, filter_prefix))
105+
end
106+
107+
return ExperimentationClient

clients/lua/main.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
local CacClient = require("cacclient.CacClient")
2+
local ExperimentationClient = require("expclient.ExperimentationClient")
3+
4+
local tenant_name = "dev"
5+
local polling_frequency = 1
6+
local cac_host_name = "http://localhost:8080"
7+
8+
local exp_client = ExperimentationClient:new(tenant_name, polling_frequency, cac_host_name)
9+
local response = exp_client:create_new_experimentation_client()
10+
print(exp_client:get_satisfied_experiments("{}",""))
11+
12+
local cac_client = CacClient:new(tenant_name, polling_frequency, cac_host_name)
13+
local response = cac_client:create_new_cac_client()
14+
print(cac_client:get_last_modified())

0 commit comments

Comments
 (0)