-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: adding go cac and experimentation client wrapper
- Loading branch information
Showing
5 changed files
with
347 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
Set directory path that contains superposition object files in <span style="color: red" > SUPERPOSITION_LIB_PATH </span> env variable; | ||
|
||
Set directory path that contains C header file <span style="color: red" > SUPERPOSITION_INCLUDE_PATH </span> env variable; | ||
|
||
<span style="color: red" > export CGO_CFLAGS="-I${SUPERPOSITION_INCLUDE_PATH}" </span> (Set this flag in order to know go where to pick C header file from). | ||
|
||
## [<u> CAC Client </u>](./cacclient/main.go) | ||
|
||
* #### <span style="color: red" > export CGO_LDFLAGS="-L${SUPERPOSITION_LIB_PATH} -lcac_client" </span> (Set this flag in order to know go where to pick superposition object files from). | ||
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/main.go#50)). | ||
|
||
|
||
## [<u> Experimentation Client </u>](./expclient/main.go) | ||
|
||
* #### <span style="color: red" > export CGO_LDFLAGS="-L${SUPERPOSITION_LIB_PATH} -lexperimentation_client" </span> (Set this flag in order to know go where to pick superposition object files from). | ||
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/main.go#55)). | ||
|
||
|
||
## [<u> Test </u>](./main.go) | ||
|
||
* #### <span style="color: red" > export CGO_LDFLAGS="-L${SUPERPOSITION_LIB_PATH} -lcac_client -lexperimentation_client" </span> (Set this to run sample project) | ||
1. To test this sample project follow below steps. | ||
* Run superposition client. | ||
* Run <u> **go run main.go** </u>. | ||
2. By Default this sample code uses [dev](./main.go#L11) tenant. | ||
3. By Default this sample code assumes superposition is running on [8080](./main.go#L13) port. | ||
3. By Default this sample code polls superposition every [1 second](./main.go#L12) port. | ||
4. This sample code creates both [CAC CLient](./main.go#15) and [Experimentation Client](./main.go#20) with above default values. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
package cacclient | ||
|
||
/* | ||
#include "libcac_client.h" | ||
*/ | ||
import "C" | ||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"strings" | ||
"unsafe" | ||
) | ||
|
||
// CacClient struct | ||
type CacClient struct { | ||
tenant string | ||
pollingFrequency int | ||
cacHostName string | ||
delimiter string | ||
} | ||
|
||
// NewCacClient creates a new CacClient | ||
func NewCacClient(tenantName string, pollingFrequency int, cacHostName string) *CacClient { | ||
return &CacClient{tenant: tenantName, pollingFrequency: pollingFrequency, cacHostName: cacHostName, delimiter: ","} | ||
} | ||
|
||
// GetCacLastErrorMessage gets the last error message from the C library | ||
func (c *CacClient) GetCacLastErrorMessage() string { | ||
return C.GoString(C.cac_last_error_message()) | ||
} | ||
|
||
// GetCacLastErrorLength gets the length of the last error message | ||
func (c *CacClient) GetCacLastErrorLength() int { | ||
return int(C.cac_last_error_length()) | ||
} | ||
|
||
// GetCacClient gets the client pointer from the C library | ||
func (c *CacClient) GetCacClient() *C.Arc_Client { | ||
return C.cac_get_client(C.CString(c.tenant)) | ||
} | ||
|
||
// CreateNewCacClient creates a new client in the C library | ||
func (c *CacClient) CreateNewCacClient() int { | ||
resp := C.cac_new_client(C.CString(c.tenant), C.ulong(c.pollingFrequency), C.CString(c.cacHostName)) | ||
if resp == 1 { | ||
errorMessage := c.GetCacLastErrorMessage() | ||
fmt.Printf("Some Error Occur while creating new client: %s\n", errorMessage) | ||
} | ||
return int(resp) | ||
} | ||
|
||
// StartCacPollingUpdate starts polling updates in a separate goroutine | ||
func (c *CacClient) StartCacPollingUpdate() { | ||
tenant := C.CString(c.tenant) | ||
go C.cac_start_polling_update(tenant) | ||
} | ||
|
||
// GetCacConfig gets the configuration from the C library | ||
func (c *CacClient) GetCacConfig(filterQuery *map[string]string, filterPrefix *[]string) string { | ||
var strFilterPrefix, strFilterQuery *string | ||
|
||
if filterPrefix != nil { | ||
val := strings.Join(*filterPrefix, c.delimiter) | ||
strFilterPrefix = &val | ||
} | ||
|
||
if filterQuery != nil { | ||
byteVal, err := json.Marshal(filterQuery) | ||
if err != nil { | ||
fmt.Println("Failed to covert json to string") | ||
return "" | ||
} | ||
strVal := string(byteVal) | ||
strFilterQuery = &strVal | ||
} | ||
clientPtr := c.GetCacClient() | ||
var fp, fq *C.char | ||
|
||
if strFilterPrefix != nil { | ||
fp = C.CString(*strFilterPrefix) | ||
} else { | ||
fp = nil | ||
} | ||
|
||
if strFilterQuery != nil { | ||
fq = C.CString(*strFilterQuery) | ||
} else { | ||
fq = nil | ||
} | ||
return C.GoString(C.cac_get_config(clientPtr, fq, fp)) | ||
} | ||
|
||
// FreeCacClient frees the client in the C library | ||
func (c *CacClient) FreeCacClient(clientPtr string) { | ||
ptr := c.GetCacClient() | ||
C.cac_free_client(ptr) | ||
} | ||
|
||
// FreeCacString frees a string in the C library | ||
func (c *CacClient) FreeCacString(s string) { | ||
cs := C.CString(s) | ||
defer C.free(unsafe.Pointer(cs)) | ||
C.cac_free_string(cs) | ||
} | ||
|
||
// GetLastModified gets the last modified timestamp from the C library | ||
func (c *CacClient) GetLastModified() string { | ||
clientPtr := c.GetCacClient() | ||
return C.GoString(C.cac_get_last_modified(clientPtr)) | ||
} | ||
|
||
// GetResolvedConfig gets the resolved configuration from the C library | ||
func (c *CacClient) GetResolvedConfig(query map[string]string, filterKeys *[]string, mergeStrategy string) string { | ||
|
||
var strfilterKeys *string | ||
|
||
if filterKeys != nil { | ||
val := strings.Join(*filterKeys, c.delimiter) | ||
strfilterKeys = &val | ||
} | ||
|
||
var fk *C.char | ||
if strfilterKeys != nil { | ||
fk = C.CString(*strfilterKeys) | ||
} else { | ||
fk = nil | ||
} | ||
clientPtr := c.GetCacClient() | ||
strQuery, err := json.Marshal(query) | ||
if err != nil { | ||
fmt.Println("Failed to covert json to string") | ||
return "" | ||
} | ||
q := C.CString(string(strQuery)) | ||
ms := C.CString(mergeStrategy) | ||
|
||
return C.GoString(C.cac_get_resolved_config(clientPtr, q, fk, ms)) | ||
} | ||
|
||
// GetDefaultConfig gets the default configuration from the C library | ||
func (c *CacClient) GetDefaultConfig(filterKeys *[]string) string { | ||
clientPtr := c.GetCacClient() | ||
var strfilterKeys *string | ||
|
||
if filterKeys != nil { | ||
val := strings.Join(*filterKeys, c.delimiter) | ||
strfilterKeys = &val | ||
} | ||
|
||
var fk *C.char | ||
if strfilterKeys != nil { | ||
fk = C.CString(*strfilterKeys) | ||
} else { | ||
fk = nil | ||
} | ||
|
||
return C.GoString(C.cac_get_default_config(clientPtr, fk)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package expclient | ||
|
||
/* | ||
#include "libexperimentation_client.h" | ||
*/ | ||
import "C" | ||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
// Define ExperimentationClient structure | ||
type ExperimentationClient struct { | ||
tenant string | ||
pollingFrequency int | ||
cacHostName string | ||
delimiter string | ||
} | ||
|
||
// Constructor | ||
func NewExperimentationClient(tenant string, pollingFrequency int, hostName string) *ExperimentationClient { | ||
return &ExperimentationClient{ | ||
tenant: tenant, | ||
pollingFrequency: pollingFrequency, | ||
cacHostName: hostName, | ||
delimiter: ",", | ||
} | ||
} | ||
|
||
// Methods | ||
func (ec *ExperimentationClient) CreateNewExperimentationClient() int { | ||
respCode := C.expt_new_client(C.CString(ec.tenant), C.ulong(ec.pollingFrequency), C.CString(ec.cacHostName)) | ||
if respCode == 1 { | ||
errorMessage := C.GoString(C.expt_last_error_message()) | ||
fmt.Printf("Error occurred while creating new experimentation client: %s\n", errorMessage) | ||
return int(respCode) | ||
} | ||
return int(respCode) | ||
} | ||
|
||
func (ec *ExperimentationClient) GetExperimentationClient() *C.Arc_Client { | ||
return C.expt_get_client(C.CString(ec.tenant)) | ||
} | ||
|
||
func (ec *ExperimentationClient) GetRunningExperiments() string { | ||
clientPtr := ec.GetExperimentationClient() | ||
return C.GoString(C.expt_get_running_experiments(clientPtr)) | ||
} | ||
|
||
func (ec *ExperimentationClient) FreeString(str string) { | ||
C.expt_free_string(C.CString(str)) | ||
} | ||
|
||
func (ec *ExperimentationClient) StartExperimentationPollingUpdate() { | ||
go C.expt_start_polling_update(C.CString(ec.tenant)) | ||
} | ||
|
||
func (ec *ExperimentationClient) GetExperimentationLastErrorLength() int { | ||
return int(C.expt_last_error_length()) | ||
} | ||
|
||
func (ec *ExperimentationClient) FreeExperimentationClient() { | ||
clientPtr := ec.GetExperimentationClient() | ||
C.expt_free_client(clientPtr) | ||
} | ||
|
||
func (ec *ExperimentationClient) GetFilteredSatisfiedExperiments(context map[string]string, filterPrefix *[]string) string { | ||
clientPtr := ec.GetExperimentationClient() | ||
strContext, err := json.Marshal(context) | ||
if err != nil { | ||
fmt.Println("Failed to covert json to string") | ||
return "" | ||
} | ||
var strFilterPrefix *string | ||
|
||
if filterPrefix != nil { | ||
val := strings.Join(*filterPrefix, ec.delimiter) | ||
strFilterPrefix = &val | ||
} | ||
|
||
var fk *C.char | ||
if strFilterPrefix != nil { | ||
fk = C.CString(*strFilterPrefix) | ||
} else { | ||
fk = nil | ||
} | ||
|
||
return C.GoString(C.expt_get_filtered_satisfied_experiments(clientPtr, C.CString(string(strContext)), fk)) | ||
} | ||
|
||
func (ec *ExperimentationClient) GetApplicableVariant(context map[string]string, toss int) string { | ||
clientPtr := ec.GetExperimentationClient() | ||
strContext, err := json.Marshal(context) | ||
if err != nil { | ||
fmt.Println("Failed to covert json to string") | ||
return "" | ||
} | ||
return C.GoString(C.expt_get_applicable_variant(clientPtr, C.CString(string(strContext)), C.short(toss))) | ||
} | ||
|
||
func (ec *ExperimentationClient) GetSatisfiedExperiments(context map[string]string, filterPrefix *[]string) string { | ||
clientPtr := ec.GetExperimentationClient() | ||
strContext, err := json.Marshal(context) | ||
var strFilterPrefix *string | ||
|
||
if filterPrefix != nil { | ||
val := strings.Join(*filterPrefix, ec.delimiter) | ||
strFilterPrefix = &val | ||
} | ||
|
||
var fk *C.char | ||
if strFilterPrefix != nil { | ||
fk = C.CString(*strFilterPrefix) | ||
} else { | ||
fk = nil | ||
} | ||
|
||
if err != nil { | ||
fmt.Println("Failed to covert json to string") | ||
return "" | ||
} | ||
return C.GoString(C.expt_get_satisfied_experiments(clientPtr, C.CString(string(strContext)), fk)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module goclients | ||
|
||
go 1.22.5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"goclients/cacclient" | ||
"goclients/expclient" | ||
) | ||
|
||
func main() { | ||
// Example usage | ||
tenant := "dev" | ||
pollingFrequency := 1 | ||
cachostName := "http://localhost:8080" | ||
|
||
client := cacclient.NewCacClient(tenant, pollingFrequency, cachostName) | ||
client.CreateNewCacClient() | ||
fmt.Println("Default Configs", client.GetCacConfig(nil, nil)) | ||
|
||
expClient := expclient.NewExperimentationClient(tenant, pollingFrequency, cachostName) | ||
expClient.CreateNewExperimentationClient() | ||
fmt.Println("Running experiments:", expClient.GetRunningExperiments()) | ||
} |