Skip to content

Commit

Permalink
feat: Error types (#30)
Browse files Browse the repository at this point in the history
* feat: use concrete error types

* chore: update readme for acknowledgement

* chore: fix tests
  • Loading branch information
markphelps authored Dec 12, 2023
1 parent be59100 commit 1cbd333
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 92 deletions.
18 changes: 18 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,21 @@ You can refer to the architecture diagram below:
<p align="center">
<img src="./diagrams/architecture.png" alt="Client SDKs Architecture" width="500px" />
</p>

## Engine

The engine is responsible for the following:

- Polling for evaluation state from the Flipt server.
- Unmarshalling the evaluation state from JSON to memory.
- Storing the evaluation state in memory per namespace.
- Evaluating context against the evaluation state and returning the results of the evaluation.

## Client SDKs

The client SDKs are responsible for the following:

- Marshalling context to JSON.
- Sending context to the engine via FFI.
- Unmarshalling the results of the evaluation from JSON to memory and returning the results to the caller.
- Providing a high-level API for the caller to interact with the client SDK.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ This repository centralizes the client-side SDKs for [Flipt](https://github.com/

These client-side SDKs are responsible for evaluating context and returning the results of the evaluation. They enable developers to easily integrate Flipt into their applications without relying on server-side evaluation.

> [!NOTE] These SDKs are currently experimental. We are looking for feedback on the design and implementation. Please open an issue if you have any feedback or questions.
> [!WARNING]
> These SDKs are currently experimental. We are looking for feedback on the design and implementation. Please open an issue if you have any feedback or questions.
## Architecture

The client SDKs are designed to be embedded in end-user applications.

The evaluation logic is written in Rust and can be found in the `flipt-engine` directory. The language clients that are used in end-user applications wrap the engine can be found in the `flipt-client-{language}` directories.
The evaluation logic is written in Rust and can be found in the [flipt-engine](./flipt-engine/) directory. The language clients that are used in end-user applications wrap the engine can be found in the `flipt-client-{language}` directories.

See [ARCHITECTURE.md](./ARCHITECTURE.md).

Expand Down Expand Up @@ -61,6 +62,14 @@ While the server SDKs performed evaluations in the range of 0-14ms, the client S

See [CONTRIBUTING.md](./CONTRIBUTING.md).

### Help Wanted

We are not Rust experts, and are constantly learning. If you see something that can be improved especially in the [flipt-engine](./flipt-engine/), please open an issue or a PR, we would love to learn from you. :heart:

## License

All code in this repository is licensed under the MIT License. See [LICENSE](./LICENSE).

## Acknowledgements

- [Unleash/yggdrasil](https://github.com/Unleash/yggdrasil) - While we independently decided upon using Rust + FFI as the engine for the client SDKs, we were inspired by the design of the yggdrasil project from Unleash.
4 changes: 2 additions & 2 deletions flipt-client-go/evaluation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func TestVariantFailure(t *testing.T) {

assert.Nil(t, variant.Result)
assert.Equal(t, "failure", variant.Status)
assert.Equal(t, "failed to get flag information default/nonexistent", variant.ErrorMessage)
assert.Equal(t, "invalid request: failed to get flag information default/nonexistent", variant.ErrorMessage)
}

func TestBooleanFailure(t *testing.T) {
Expand All @@ -83,5 +83,5 @@ func TestBooleanFailure(t *testing.T) {

assert.Nil(t, boolean.Result)
assert.Equal(t, "failure", boolean.Status)
assert.Equal(t, "failed to get flag information default/nonexistent", boolean.ErrorMessage)
assert.Equal(t, "invalid request: failed to get flag information default/nonexistent", boolean.ErrorMessage)
}
4 changes: 2 additions & 2 deletions flipt-client-node/src/evaluation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ test('variant failure', () => {
expect(variant.result).toBeNull();
expect(variant.status).toEqual('failure');
expect(variant.error_message).toEqual(
'failed to get flag information default/nonexistent'
'invalid request: failed to get flag information default/nonexistent'
);
});

Expand All @@ -78,6 +78,6 @@ test('boolean failure', () => {
expect(boolean.result).toBeNull();
expect(boolean.status).toEqual('failure');
expect(boolean.error_message).toEqual(
'failed to get flag information default/nonexistent'
'invalid request: failed to get flag information default/nonexistent'
);
});
6 changes: 4 additions & 2 deletions flipt-client-python/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ def test_failure_variant(self):
)
self.assertIsNone(variant.result)
self.assertEqual(
variant.error_message, "failed to get flag information default/nonexistent"
variant.error_message,
"invalid request: failed to get flag information default/nonexistent",
)
self.assertEqual("failure", variant.status)

Expand All @@ -56,7 +57,8 @@ def test_failure_boolean(self):
)
self.assertIsNone(boolean.result)
self.assertEqual(
boolean.error_message, "failed to get flag information default/nonexistent"
boolean.error_message,
"invalid request: failed to get flag information default/nonexistent",
)
self.assertEqual("failure", boolean.status)

Expand Down
8 changes: 4 additions & 4 deletions flipt-client-ruby/lib/evaluation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ module Flipt
class Error < StandardError; end

class EvaluationClient
extend FFI::Library

FLIPTENGINE = 'ext/libfliptengine'

def self.platform_specific_lib
def self.libfile
case RbConfig::CONFIG['host_os']
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
"#{FLIPTENGINE}.dll"
Expand All @@ -23,8 +25,7 @@ def self.platform_specific_lib
end
end

extend FFI::Library
ffi_lib File.expand_path(platform_specific_lib, __dir__)
ffi_lib File.expand_path(libfile, __dir__)

# void *initialize_engine(const char *const *namespaces, const char *opts);
attach_function :initialize_engine, %i[pointer string], :pointer
Expand Down Expand Up @@ -66,7 +67,6 @@ def evaluate_boolean(evaluation_request = {})
end

private

def validate_evaluation_request(evaluation_request)
if evaluation_request[:entity_id].nil? || evaluation_request[:entity_id].empty?
raise ArgumentError, 'entity_id is required'
Expand Down
4 changes: 2 additions & 2 deletions flipt-client-ruby/spec/evaluation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
expect(resp).to_not be_nil
expect(resp['result']).to be_nil
expect(resp['status']).to eq('failure')
expect(resp['error_message']).to eq('failed to get flag information default/nonexistent')
expect(resp['error_message']).to eq('invalid request: failed to get flag information default/nonexistent')
end
end

Expand All @@ -65,7 +65,7 @@
expect(resp).to_not be_nil
expect(resp['result']).to be_nil
expect(resp['status']).to eq('failure')
expect(resp['error_message']).to eq('failed to get flag information default/nonexistent')
expect(resp['error_message']).to eq('invalid request: failed to get flag information default/nonexistent')
end
end
end
2 changes: 1 addition & 1 deletion flipt-engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.89", features = ["raw_value"] }
uuid = { version = "1.5.0", features = ["v4"] }
crc32fast = "1.3.2"
snafu = "0.7.5"
chrono = { version = "0.4.31", features = ["serde", "rustc-serialize"] }
futures = "0.3"
thiserror = "1.0.50"

[build-dependencies]
cbindgen = "0.26.0"
Expand Down
15 changes: 15 additions & 0 deletions flipt-engine/src/error/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
#[error("error engine null pointer")]
NullPointer,
#[error("error parsing json")]
InvalidJSON(#[from] serde_json::Error),
#[error("invalid request: {0}")]
InvalidRequest(String),
#[error("server error: {0}")]
ServerError(String),
#[error("unknown error: {0}")]
Unknown(String),
}
Loading

0 comments on commit 1cbd333

Please sign in to comment.