Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
36ff06d
wip: client side rewrite
gagantrivedi Apr 4, 2022
a7c391d
wip: create thread to update environment
gagantrivedi Apr 4, 2022
4f63ead
wip: add async t
gagantrivedi Apr 4, 2022
cfbf520
wip: update analytics processor to use channels
gagantrivedi Apr 4, 2022
e3d33ce
update lib.rs
gagantrivedi Apr 4, 2022
4547ac0
wip: get_identity_flags: Add signature
gagantrivedi Apr 4, 2022
aa4cc5e
fix(models/Flag): use flagsmith value type
gagantrivedi Apr 4, 2022
8e8be61
fixup! wip: get_identity_flags: Add signature
gagantrivedi Apr 4, 2022
7dcb2ba
rewrite error module
gagantrivedi Apr 4, 2022
5f2f66f
fix(analytics): make new public
gagantrivedi Apr 5, 2022
417c88b
expand public interface and add default handler and analytics
gagantrivedi Apr 5, 2022
22fe7cb
fix references
gagantrivedi Apr 5, 2022
65976bb
Add analytics to models
gagantrivedi Apr 5, 2022
242c06b
update author
gagantrivedi Apr 7, 2022
74f892e
misc: improve interface remove redundant code
gagantrivedi Apr 7, 2022
6f8c856
use flag engine from github
gagantrivedi Apr 7, 2022
45ceac4
make traits optional for get_identity_flags
gagantrivedi Apr 7, 2022
b69c0f4
wip: Add example
gagantrivedi Apr 7, 2022
d2918f9
Add default trait to Flag
gagantrivedi Apr 10, 2022
2c95125
update example with default handler
gagantrivedi Apr 10, 2022
725581a
exit threads on flagsmith client being dropped
gagantrivedi Apr 10, 2022
e427d9a
fix warnings
gagantrivedi Apr 11, 2022
00af313
update example: add readme
gagantrivedi Apr 12, 2022
1fef718
minor refactoring
gagantrivedi Apr 12, 2022
e81987e
Add tests for Flag
gagantrivedi Apr 12, 2022
1a73379
fix(*._identity_flags): use &str for identifier
gagantrivedi Apr 13, 2022
f666eee
feat(flag/model): Add a method to get value as string
gagantrivedi Apr 13, 2022
2e0f8ee
wip: Add integration tests
gagantrivedi Apr 13, 2022
07f5f8f
tests(analytics_processor): Add tests and minor tweaks
gagantrivedi Apr 16, 2022
d183409
tests: Add fixtures
gagantrivedi Apr 16, 2022
266176b
feat(flag): Add value_as_type getters
gagantrivedi Apr 17, 2022
6f4a0dc
test(polling): Add tests for polling thread and minor fixes
gagantrivedi Apr 17, 2022
b5ff9d4
fix(default_handler): Add tests
gagantrivedi Apr 17, 2022
df9cd58
test(error/mod): Add tests for errors
gagantrivedi Apr 17, 2022
39f70ce
expose segments
gagantrivedi Apr 25, 2022
20ecdfd
Add flag engine
gagantrivedi May 27, 2022
147454a
update flag engine crate in example cargo.toml
gagantrivedi May 27, 2022
6120801
fix typo
gagantrivedi May 27, 2022
b8966df
bump flag-engine
gagantrivedi May 27, 2022
6688102
Update default url to edge
gagantrivedi Jun 1, 2022
cbbf8ce
fixup! bump version
gagantrivedi Jun 1, 2022
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
14 changes: 11 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "flagsmith"
version = "1.0.0"
authors = ["Tomasz Zdybał <tomek@zdybal.lap.pl>"]
edition = "2018"
authors = ["Gagan Trivedi <gagan.trivedi@flagsmith.com>"]
edition = "2021"
license = "BSD-3-Clause"
description = "Flagsmith SDK for Rust"
homepage = "https://flagsmith.com/"
Expand All @@ -16,5 +16,13 @@ keywords = ["Flagsmith", "feature-flag", "remote-config"]
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = { version = "^0.10", features = ["blocking","json"] }
reqwest = { version = "0.11", features = ["json", "blocking"] }
url = "2.1"
chrono = { version = "0.4"}
log = "0.4"

flagsmith-flag-engine = "0.1.1"

[dev-dependencies]
httpmock = "0.6"
rstest = "0.12.0"
19 changes: 19 additions & 0 deletions example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "example"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[net]
git-fetch-with-cli = true # use the `git` executable for git operations

[dependencies]
rocket = "0.4.10"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
rocket_contrib = {version = "0.4.10", features=["tera_templates"]}
flagsmith = {path="../"}

flagsmith-flag-engine = "0.1.0"

3 changes: 3 additions & 0 deletions example/Rocket.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[global]
port = 5000
workers = 1
21 changes: 21 additions & 0 deletions example/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Flagsmith Basic Rust Example

This directory contains a basic Rocket application which utilises Flagsmith. To run the example application, you'll
need to go through the following steps:

1. Create an account, organisation and project on [Flagsmith](https://flagsmith.com)
2. Create a feature in the project called "secret_button"
3. Give the feature a value using the json editor as follows:

```json
{"colour": "#ababab"}
```

4. Set the environment variable `FLAGSMITH_ENVIRONMENT_KEY` with the environment key of one of the environments
in flagsmith (This can be found on the 'settings' page accessed from the menu on the left under the chosen environment.)
5. Run the app using `cargo run`
6. Browse to http://localhost:5000

Now you can play around with the 'secret_button' feature in flagsmith, turn it on to show it and edit the colour in the
json value to edit the colour of the button. You can also identify as a given user and then update the settings for the
secret button feature for that user in the flagsmith interface to see the affect that has too.
92 changes: 92 additions & 0 deletions example/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use]
extern crate rocket;
#[macro_use]
extern crate serde_derive;
extern crate flagsmith;
extern crate rocket_contrib;
extern crate serde_json;

use std::env;

use rocket_contrib::templates::Template;

use flagsmith::{Flag, Flagsmith, FlagsmithOptions};
use flagsmith_flag_engine::identities::Trait;
use flagsmith_flag_engine::types::{FlagsmithValue, FlagsmithValueType};

#[derive(Serialize)]
struct TemplateContext {
show_button: bool,
button_colour: String,
identifier: String,
}
fn default_flag_handler(feature_name: &str) -> Flag {
let mut flag: Flag = Default::default();
if feature_name == "secret_button" {
flag.value.value_type = FlagsmithValueType::String;
flag.value.value = serde_json::json!({"colour": "#b8b8b8"}).to_string();
}
return flag;
}

use std::{thread, time::Duration};
#[get("/?<identifier>&<trait_key>&<trait_value>")]
fn home(
identifier: Option<String>,
trait_key: Option<String>,
trait_value: Option<String>,
) -> Template {
let options = FlagsmithOptions {
default_flag_handler: Some(default_flag_handler),
enable_local_evaluation: true,
..Default::default()
};

let flagsmith = Flagsmith::new(env::var("FLAGSMITH_ENVIRONMENT_KEY").expect("FLAGSMITH_ENVIRONMENT_KEY not found in environment"), options);
let flags;
if identifier.is_some() {
let traits = match trait_key {
Some(trait_key) if trait_key != "".to_string() => Some(vec![Trait {
trait_key,
trait_value: FlagsmithValue {
value: trait_value.unwrap_or("".to_string()),
value_type: FlagsmithValueType::None,
},
}]),
Some(_) => None,
None => None,
};
flags = flagsmith
.get_identity_flags(identifier.as_ref().unwrap().to_string(), traits)
.unwrap();
} else {
// Get the default flags for the current environment
flags = flagsmith.get_environment_flags().unwrap();
}

let show_button = flags.is_feature_enabled("secret_button").unwrap();
let button_data = flags.get_feature_value_as_string("secret_button").unwrap();

let button_json: serde_json::Value = serde_json::from_str(&button_data).unwrap();
let button_colour = button_json["colour"].as_str().unwrap().to_string();

let context = TemplateContext {
show_button,
button_colour,
identifier: identifier.unwrap_or("World".to_string()),
};

Template::render("home", &context)
}

fn rocket() -> rocket::Rocket {
rocket::ignite()
.mount("/", routes![home])
.attach(Template::fairing())
}

fn main() {
rocket().launch();
}
25 changes: 25 additions & 0 deletions example/templates/home.html.tera
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<head>
<style></style>
</head>
<title>Flagsmith Example</title>
<body>
<p>Hello, {{ identifier }}.</p>
{% if show_button %}
<button style="background-color: {{ button_colour }}">A secret button</button>
{% endif %}

<p></p>

<form action="/" method="GET">
<h3>Identify as a user</h3>
<label for="identifier">Identifier: </label><input name="identifier" id="identifier"><br>

<p>... with an optional user trait</p>
<label for="trait_key">Trait key: </label><input name="trait_key" id="trait_key"><br>
<label for="trait_value">Trait value: </label><input name="trait_value" id="trait_value"><br><br>

<button type="submit">Identify!</button>
</form>

</body>
44 changes: 20 additions & 24 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,47 @@ use std::fmt;
/// Wraps several types of errors.
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
desc: String,
pub kind: ErrorKind,
pub msg: String,
}

/// Defines error kind.
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum ErrorKind {
ParseError,
RequestError,
AppError,
FlagsmithClientError,
FlagsmithAPIError,
}
impl Error{
pub fn new(kind: ErrorKind, msg: String) -> Error{
Error{
kind,
msg
}
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.kind {
ErrorKind::ParseError => write!(f, "URL parsing error: {}", &self.desc),
ErrorKind::RequestError => write!(f, "REST API request error: {}", &self.desc),
ErrorKind::AppError => write!(f, "Application error: {}", &self.desc),
ErrorKind::FlagsmithClientError => write!(f, "Flagsmith API error: {}", &self.msg),
ErrorKind::FlagsmithAPIError => write!(f, "Flagsmith client error: {}", &self.msg),
}
}
}

impl From<url::ParseError> for Error {
fn from(e: url::ParseError) -> Self {
Error {
kind: ErrorKind::ParseError,
desc: e.to_string(),
}
Error::new(ErrorKind::FlagsmithClientError, e.to_string())
}
}

impl From<reqwest::Error> for Error {
fn from(e: reqwest::Error) -> Self {
Error {
kind: ErrorKind::RequestError,
desc: e.to_string(),
}
Error::new(ErrorKind::FlagsmithAPIError, e.to_string())
}
}

impl From<String> for Error {
fn from(s: String) -> Self {
Error {
kind: ErrorKind::AppError,
desc: s,
}
impl From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Self {
Error::new(ErrorKind::FlagsmithAPIError, e.to_string())
}
}
Loading