Skip to content

Commit

Permalink
feat: Client refs (#104)
Browse files Browse the repository at this point in the history
* feat(wip): add reference option to http parser

* chore: pass through alll the languages

* chore: oops wrong url copilot

* chore: fix python test; make it fail on failure

* fix: python for x86
  • Loading branch information
markphelps authored Jan 18, 2024
1 parent fac9d03 commit de4fc26
Show file tree
Hide file tree
Showing 13 changed files with 141 additions and 23 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.git
9 changes: 9 additions & 0 deletions flipt-client-go/evaluation.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Client struct {
namespace string
url string
authToken string
ref string
updateInterval int
}

Expand All @@ -38,6 +39,7 @@ func NewClient(opts ...clientOption) (*Client, error) {
URL: client.url,
UpdateInterval: client.updateInterval,
AuthToken: client.authToken,
Reference: client.ref,
}

b, err := json.Marshal(engOpts)
Expand Down Expand Up @@ -94,6 +96,13 @@ func WithAuthToken(authToken string) clientOption {
}
}

// WithReference allows for specifying a reference to fetch flag state data from.
func WithReference(ref string) clientOption {
return func(c *Client) {
c.ref = ref
}
}

// EvaluateVariant makes an evaluation on a variant flag.
func (e *Client) EvaluateVariant(_ context.Context, flagKey, entityID string, evalContext map[string]string) (*VariantResult, error) {
ereq, err := json.Marshal(evaluationRequest{
Expand Down
1 change: 1 addition & 0 deletions flipt-client-go/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type EngineOpts struct {
URL string `json:"url,omitempty"`
AuthToken string `json:"auth_token,omitempty"`
UpdateInterval int `json:"update_interval,omitempty"`
Reference string `json:"reference,omitempty"`
}

type VariantEvaluationResponse struct {
Expand Down
3 changes: 2 additions & 1 deletion flipt-client-node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export class FliptEvaluationClient {
engine_opts: EngineOpts = {
url: 'http://localhost:8080',
update_interval: 120,
auth_token: ''
auth_token: '',
reference: ''
}
) {
const buf = Buffer.concat([allocCString(namespace ?? 'default')]);
Expand Down
1 change: 1 addition & 0 deletions flipt-client-node/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface EngineOpts {
url?: string;
update_interval?: number;
auth_token?: string;
reference?: string;
}

interface VariantEvaluationResponse {
Expand Down
2 changes: 1 addition & 1 deletion flipt-client-python/flipt_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(
)
elif platform.system() == "Linux":
arch = platform.machine()
if arch == "x86":
if arch == "x86_64":
libfile = "linux_x86_64/libfliptengine.so"
elif arch == "arm64" or arch == "aarch64":
libfile = "linux_arm64/libfliptengine.so"
Expand Down
1 change: 1 addition & 0 deletions flipt-client-python/flipt_client/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class EngineOpts(BaseModel):
url: Optional[str] = None
update_interval: Optional[int] = None
auth_token: Optional[str] = None
reference: Optional[str] = None


class VariantEvaluationResponse(BaseModel):
Expand Down
2 changes: 1 addition & 1 deletion flipt-client-python/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ def test():
Run all unittests. Equivalent to:
`poetry run python -m unittest tests`
"""
subprocess.run(["python", "-m", "unittest", "tests"])
subprocess.run(["python", "-m", "unittest", "tests"], check=True)
2 changes: 1 addition & 1 deletion flipt-client-python/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def test_boolean_no_context(self):
boolean = self.flipt_client.evaluate_boolean("flag_boolean", "someentity")
self.assertIsNone(boolean.error_message)
self.assertEqual("success", boolean.status)
self.assertFalse(boolean.result.enabled)
self.assertTrue(boolean.result.enabled)
self.assertEqual("flag_boolean", boolean.result.flag_key)


Expand Down
20 changes: 20 additions & 0 deletions flipt-client-ruby/lib/flipt_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ def self.libfile
# const char *evaluate_boolean(void *engine_ptr, const char *evaluation_request);
attach_function :evaluate_boolean, %i[pointer string], :string

# Create a new Flipt client
#
# @param namespace [String] namespace
# @param opts [Hash] options
# @option opts [String] :url Flipt server url
# @option opts [String] :auth_token Flipt api key
# @option opts [Integer] :update_interval interval in seconds to update the cache
# @option opts [String] :reference reference to use for namespace data
def initialize(namespace = 'default', opts = {})
@namespace = namespace
namespace_list = [namespace]
Expand All @@ -52,13 +60,25 @@ def self.finalize(engine)
proc { destroy_engine(engine) }
end

# Evaluate a variant flag for a given request
#
# @param evaluation_request [Hash] evaluation request
# @option evaluation_request [String] :entity_id entity id
# @option evaluation_request [String] :flag_key flag key
# @option evaluation_request [String] :namespace_key override namespace key
def evaluate_variant(evaluation_request = {})
evaluation_request[:namespace_key] = @namespace
validate_evaluation_request(evaluation_request)
resp = self.class.evaluate_variant(@engine, evaluation_request.to_json)
JSON.parse(resp)
end

# Evaluate a boolean flag for a given request
#
# @param evaluation_request [Hash] evaluation request
# @option evaluation_request [String] :entity_id entity id
# @option evaluation_request [String] :flag_key flag key
# @option evaluation_request [String] :namespace_key override namespace key
def evaluate_boolean(evaluation_request = {})
evaluation_request[:namespace_key] = @namespace
validate_evaluation_request(evaluation_request)
Expand Down
6 changes: 4 additions & 2 deletions flipt-engine-ffi/examples/evaluation.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// cargo run --example evaluation

use fliptengine::{self};
use fliptevaluation::parser::HTTPParser;
use fliptevaluation::parser::HTTPParserBuilder;
use fliptevaluation::{EvaluationRequest, Evaluator};
use std::collections::HashMap;

fn main() {
let evaluator = Evaluator::new_snapshot_evaluator(
vec!["default".into()],
HTTPParser::new("http://localhost:8080", Some("secret")),
HTTPParserBuilder::new("http://localhost:8080")
.auth_token("secret")
.build(),
)
.unwrap();

Expand Down
20 changes: 18 additions & 2 deletions flipt-engine-ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use fliptevaluation::error::Error;
use fliptevaluation::parser::HTTPParser;
use fliptevaluation::parser::{HTTPParser, HTTPParserBuilder};
use fliptevaluation::store::Snapshot;
use fliptevaluation::{
BooleanEvaluationResponse, EvaluationRequest, Evaluator, VariantEvaluationResponse,
Expand Down Expand Up @@ -69,6 +69,7 @@ pub struct EngineOpts {
url: Option<String>,
auth_token: Option<String>,
update_interval: Option<u64>,
reference: Option<String>,
}

impl Default for EngineOpts {
Expand All @@ -77,6 +78,7 @@ impl Default for EngineOpts {
url: Some("http://localhost:8080".into()),
auth_token: None,
update_interval: Some(120),
reference: None,
}
}
}
Expand Down Expand Up @@ -180,8 +182,22 @@ pub unsafe extern "C" fn initialize_engine(
.unwrap_or("http://localhost:8080".into());

let auth_token = engine_opts.auth_token.to_owned();
let reference = engine_opts.reference.to_owned();

let mut parser_builder = HTTPParserBuilder::new(&http_url);

parser_builder = match auth_token {
Some(token) => parser_builder.auth_token(&token),
None => parser_builder,
};

parser_builder = match reference {
Some(reference) => parser_builder.reference(&reference),
None => parser_builder,
};

let parser = parser_builder.build();

let parser = HTTPParser::new(&http_url, auth_token.clone().as_deref());
let evaluator = Evaluator::new_snapshot_evaluator(namespaces_vec, parser).unwrap();

Box::into_raw(Box::new(Engine::new(evaluator, engine_opts))) as *mut c_void
Expand Down
95 changes: 80 additions & 15 deletions flipt-evaluation/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,62 @@ pub struct HTTPParser {
http_client: reqwest::blocking::Client,
http_url: String,
auth_token: Option<String>,
reference: Option<String>,
}

impl HTTPParser {
// TODO: potentially use builder pattern if we need to add more options
pub fn new(url: &str, auth_token: Option<&str>) -> Self {
// We will allow the following line to panic when an error is encountered.
let http_client = reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()
.unwrap();
pub struct HTTPParserBuilder {
http_url: String,
auth_token: Option<String>,
reference: Option<String>,
}

impl HTTPParserBuilder {
pub fn new(http_url: &str) -> Self {
Self {
http_client,
http_url: url.to_string(),
auth_token: auth_token.unwrap_or_default().to_string().into(),
http_url: http_url.to_string(),
auth_token: None,
reference: None,
}
}

pub fn auth_token(mut self, auth_token: &str) -> Self {
self.auth_token = Some(auth_token.to_string());
self
}

pub fn reference(mut self, reference: &str) -> Self {
self.reference = Some(reference.to_string());
self
}

pub fn build(self) -> HTTPParser {
HTTPParser {
http_client: reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()
.unwrap(),
http_url: self.http_url,
auth_token: self.auth_token,
reference: self.reference,
}
}
}

impl HTTPParser {
fn url(&self, namespace: &str) -> String {
match &self.reference {
Some(reference) => {
format!(
"{}/internal/v1/evaluation/snapshot/namespace/{}?reference={}",
self.http_url, namespace, reference,
)
}
None => {
format!(
"{}/internal/v1/evaluation/snapshot/namespace/{}",
self.http_url, namespace
)
}
}
}
}
Expand All @@ -49,10 +90,7 @@ impl Parser for HTTPParser {

let response = match self
.http_client
.get(format!(
"{}/internal/v1/evaluation/snapshot/namespace/{}",
self.http_url, namespace
))
.get(self.url(namespace))
.headers(headers)
.send()
{
Expand Down Expand Up @@ -116,3 +154,30 @@ impl Parser for TestParser {
Ok(document)
}
}

#[cfg(test)]
mod tests {
#[test]
fn test_http_parser_url() {
use super::HTTPParserBuilder;

let parser = HTTPParserBuilder::new("http://localhost:8080")
.auth_token("secret")
.reference("ref")
.build();

assert_eq!(
parser.url("default"),
"http://localhost:8080/internal/v1/evaluation/snapshot/namespace/default?reference=ref"
);

let parser = HTTPParserBuilder::new("http://localhost:8080")
.auth_token("secret")
.build();

assert_eq!(
parser.url("default"),
"http://localhost:8080/internal/v1/evaluation/snapshot/namespace/default"
);
}
}

0 comments on commit de4fc26

Please sign in to comment.