kameleoon-openfeature-ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +171 -0
- data/lib/kameleoon/data_converter.rb +61 -0
- data/lib/kameleoon/kameleoon_provider.rb +146 -0
- data/lib/kameleoon/resolver.rb +117 -0
- data/lib/kameleoon/types.rb +28 -0
- data/lib/kameleoon/util/version.rb +5 -0
- metadata +77 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: aeeddfee1813205e7afa99fe74684a056233268d96383fcb5c98a9e2d555b183
|
4
|
+
data.tar.gz: e1b9e9e7353c54e9b0c54d4481b13b9eccf19ccded1171bd51b4ac840cb3940e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 796225f66e0bc342064a7bd647fbb4788e4208c583aa40dd7445a0e706438d7f722858acc6f2ab81df55197af811f9936d3dc84c57c83c0e2621df32d6944753
|
7
|
+
data.tar.gz: b947424769ab79c1a3fd45fffec0aa6da3e999d14baf6123a2abfdee861653d7f5db0ec65ba3ab6a458e4ce8b0465628686b81f28db954be16e386097ac6d564
|
data/README.md
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
# Kameleoon OpenFeature provider for Ruby
|
2
|
+
|
3
|
+
The Kameleoon OpenFeature provider for Ruby allows you to connect your OpenFeature Ruby implementation to Kameleoon without installing the Ruby Kameleoon SDK.
|
4
|
+
|
5
|
+
> [!WARNING]
|
6
|
+
> This is a beta version. Breaking changes may be introduced before general release.
|
7
|
+
|
8
|
+
## Supported Ruby versions
|
9
|
+
|
10
|
+
This version of the SDK is built for the following targets:
|
11
|
+
|
12
|
+
* Ruby 3.1.4 and above.
|
13
|
+
|
14
|
+
## Get started
|
15
|
+
|
16
|
+
This section explains how to install, configure, and customize the Kameleoon OpenFeature provider.
|
17
|
+
|
18
|
+
### Install dependencies
|
19
|
+
|
20
|
+
First, install the required dependencies in your application.
|
21
|
+
|
22
|
+
```sh
|
23
|
+
gem install bundler
|
24
|
+
bundle install
|
25
|
+
```
|
26
|
+
|
27
|
+
### Usage
|
28
|
+
|
29
|
+
The following example shows how to use the Kameleoon provider with the OpenFeature SDK.
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'kameleoon-client'
|
33
|
+
require 'open_feature/sdk'
|
34
|
+
|
35
|
+
client_config = Kameleoon::KameleoonClientConfig.new(
|
36
|
+
'clientId',
|
37
|
+
'clientSecret',
|
38
|
+
top_level_domain: 'topLevelDomain',
|
39
|
+
)
|
40
|
+
|
41
|
+
provider = Kameleoon::KameleoonProvider.new('siteCode', config: client_config)
|
42
|
+
OpenFeature::SDK.configure do |config|
|
43
|
+
config.set_provider(provider)
|
44
|
+
end
|
45
|
+
|
46
|
+
client = OpenFeature::SDK.build_client
|
47
|
+
|
48
|
+
data_dictionary = {
|
49
|
+
'targeting_key' => 'visitorCode',
|
50
|
+
'variableKey' => 'variableKey'
|
51
|
+
}
|
52
|
+
eval_context = OpenFeature::SDK::EvaluationContext.new(**data_dictionary)
|
53
|
+
|
54
|
+
resolution_details = client.fetch_integer_value(flag_key: 'featureKey', default_value: 5,
|
55
|
+
evaluation_context: eval_context)
|
56
|
+
number_of_recommended_products = resolution_details.value
|
57
|
+
|
58
|
+
puts "Number of recommended products: #{number_of_recommended_products}"
|
59
|
+
```
|
60
|
+
|
61
|
+
#### Customize the Kameleoon provider
|
62
|
+
|
63
|
+
You can customize the Kameleoon provider by changing the `KameleoonClientConfig` object that you passed to the constructor above. For example:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
client_config = Kameleoon::KameleoonClientConfig.new(
|
67
|
+
'clientId',
|
68
|
+
'clientSecret',
|
69
|
+
top_level_domain: 'topLevelDomain',
|
70
|
+
refresh_interval_minute: 1, # Optional field
|
71
|
+
session_duration_minute: 5, # Optional field
|
72
|
+
)
|
73
|
+
|
74
|
+
provider = Kameleoon::KameleoonProvider.new('siteCode', config: client_config)
|
75
|
+
```
|
76
|
+
> [!NOTE]
|
77
|
+
> For additional configuration options, see the [Kameleoon documentation](https://developers.kameleoon.com/feature-management-and-experimentation/web-sdks/ruby-sdk/#example-code).
|
78
|
+
|
79
|
+
## EvaluationContext and Kameleoon Data
|
80
|
+
|
81
|
+
Kameleoon uses the concept of associating `Data` to users, while the OpenFeature SDK uses the concept of an `EvaluationContext`, which is a dictionary of string keys and values. The Kameleoon provider maps the `EvaluationContext` to the Kameleoon `Data`.
|
82
|
+
|
83
|
+
> [!NOTE]
|
84
|
+
> To get the evaluation for a specific visitor, set the `targeting_key` value for the `EvaluationContext` to the visitor code (user ID). If the value is not provided, then the `defaultValue` parameter will be returned.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
values = { 'targeting_key' => 'userId' }
|
88
|
+
|
89
|
+
eval_context = OpenFeature::SDK::EvaluationContext.new(**values)
|
90
|
+
```
|
91
|
+
|
92
|
+
The Kameleoon provider provides a few predefined parameters that you can use to target a visitor from a specific audience and track each conversion. These are:
|
93
|
+
|
94
|
+
| Parameter | Description |
|
95
|
+
|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
96
|
+
| `Data::Type::CUSTOM_DATA` | The parameter is used to set [`CustomData`](https://developers.kameleoon.com/feature-management-and-experimentation/web-sdks/ruby-sdk/#customdata) for a visitor. |
|
97
|
+
| `Data::Type::CONVERSION` | The parameter is used to track a [`Conversion`](https://developers.kameleoon.com/feature-management-and-experimentation/web-sdks/ruby-sdk/#conversion) for a visitor. |
|
98
|
+
|
99
|
+
### Data::Type::CUSTOM_DATA
|
100
|
+
|
101
|
+
Use `Data::Type::CUSTOM_DATA` to set [`CustomData`](https://developers.kameleoon.com/feature-management-and-experimentation/web-sdks/ruby-sdk/#customdata) for a visitor. The `Data::Type::CUSTOM_DATA` field has the following parameters:
|
102
|
+
|
103
|
+
| Parameter | Type | Description |
|
104
|
+
|--------------------------------|---------|-------------------------------------------------------------------|
|
105
|
+
| `Data::CustomDataType::INDEX` | Integer | Index or ID of the custom data to store. This field is mandatory. |
|
106
|
+
| `Data::CustomDataType::VALUES` | String | Value of the custom data to store. This field is mandatory. |
|
107
|
+
|
108
|
+
#### Example
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
custom_data_dictionary = {
|
112
|
+
'targeting_key' => 'userId',
|
113
|
+
Kameleoon::Types::Data::Type::CUSTOM_DATA => {
|
114
|
+
Kameleoon::Types::Data::CustomDataType::INDEX => 1,
|
115
|
+
Kameleoon::Types::Data::CustomDataType::VALUES => '10'
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
eval_context = OpenFeature::SDK::EvaluationContext.new(**custom_data_dictionary)
|
120
|
+
```
|
121
|
+
|
122
|
+
### Data::Type::CONVERSION
|
123
|
+
|
124
|
+
Use `Data::Type::CONVERSION` to track a [`Conversion`](https://developers.kameleoon.com/feature-management-and-experimentation/web-sdks/ruby-sdk/#conversion) for a visitor. The `Data::Type::CONVERSION` field has the following parameters:
|
125
|
+
|
126
|
+
| Parameter | Type | Description |
|
127
|
+
|---------------------------------|---------|-----------------------------------------------------------------|
|
128
|
+
| `Data::ConversionType::GOAL_ID` | Integer | Identifier of the goal. This field is mandatory. |
|
129
|
+
| `Data::ConversionType::REVENUE` | Float | Revenue associated with the conversion. This field is optional. |
|
130
|
+
|
131
|
+
#### Example
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
conversion_dictionary = {
|
135
|
+
Kameleoon::Types::Data::ConversionType::GOAL_ID => 1,
|
136
|
+
Kameleoon::Types::Data::ConversionType::REVENUE => 200
|
137
|
+
}
|
138
|
+
|
139
|
+
eval_context = OpenFeature::SDK::EvaluationContext.new(**{
|
140
|
+
'targeting_key' => 'userId',
|
141
|
+
Kameleoon::Types::Data::Type::CONVERSION => conversion_dictionary
|
142
|
+
})
|
143
|
+
```
|
144
|
+
|
145
|
+
### Use multiple Kameleoon Data types
|
146
|
+
|
147
|
+
You can provide many different kinds of Kameleoon data within a single `EvaluationContext` instance.
|
148
|
+
|
149
|
+
For example, the following code provides one `Data::Type::CONVERSION` instance and two `Data::Type::CUSTOM_DATA` instances.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
data_dictionary = {
|
153
|
+
'targeting_key' => 'userId',
|
154
|
+
Kameleoon::Types::Data::Type::CONVERSION => {
|
155
|
+
Kameleoon::Types::Data::ConversionType::GOAL_ID => 1,
|
156
|
+
Kameleoon::Types::Data::ConversionType::REVENUE => 200
|
157
|
+
},
|
158
|
+
Kameleoon::Types::Data::Type::CUSTOM_DATA => [
|
159
|
+
{
|
160
|
+
Kameleoon::Types::Data::CustomDataType::INDEX => 1,
|
161
|
+
Kameleoon::Types::Data::CustomDataType::VALUES => ['10', '30']
|
162
|
+
},
|
163
|
+
{
|
164
|
+
Kameleoon::Types::Data::CustomDataType::INDEX => 2,
|
165
|
+
Kameleoon::Types::Data::CustomDataType::VALUES => '20'
|
166
|
+
}
|
167
|
+
]
|
168
|
+
}
|
169
|
+
|
170
|
+
eval_context = OpenFeature::SDK::EvaluationContext.new(**data_dictionary)
|
171
|
+
```
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/types'
|
4
|
+
require 'kameleoon/data/conversion'
|
5
|
+
require 'kameleoon/data/custom_data'
|
6
|
+
|
7
|
+
module Kameleoon
|
8
|
+
# DataConverter is used to convert data from OpenFeature to Kameleoon.
|
9
|
+
class DataConverter
|
10
|
+
class << self
|
11
|
+
def conversion_methods
|
12
|
+
# Dictionary which contains conversion methods by keys
|
13
|
+
@conversion_methods ||= {
|
14
|
+
Kameleoon::Types::Data::Type::CONVERSION => method(:make_conversion),
|
15
|
+
Kameleoon::Types::Data::Type::CUSTOM_DATA => method(:make_custom_data)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
# ToKameleoon converts EvaluationContext to Kameleoon SDK data types.
|
20
|
+
def to_kameleoon(context)
|
21
|
+
return [] if context.nil?
|
22
|
+
|
23
|
+
data = []
|
24
|
+
context.fields.each do |key, value|
|
25
|
+
method = conversion_methods[key]
|
26
|
+
next if method.nil? || value.nil?
|
27
|
+
values = value.is_a?(Array) ? value : [value]
|
28
|
+
values.each do |val|
|
29
|
+
data << method.call(val)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
data
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# make_conversion creates a Conversion object from the value.
|
38
|
+
def make_conversion(value)
|
39
|
+
return nil unless value.is_a?(Hash)
|
40
|
+
|
41
|
+
goal_id = value[Kameleoon::Types::Data::ConversionType::GOAL_ID]
|
42
|
+
revenue = value[Kameleoon::Types::Data::ConversionType::REVENUE]
|
43
|
+
revenue = revenue.to_f if revenue.is_a?(Integer)
|
44
|
+
revenue ||= 0.0
|
45
|
+
|
46
|
+
Kameleoon::Conversion.new(goal_id, revenue, false)
|
47
|
+
end
|
48
|
+
|
49
|
+
# make_custom_data creates a CustomData object from the value.
|
50
|
+
def make_custom_data(value)
|
51
|
+
return nil unless value.is_a?(Hash)
|
52
|
+
|
53
|
+
index = value[Kameleoon::Types::Data::CustomDataType::INDEX]
|
54
|
+
values = value[Kameleoon::Types::Data::CustomDataType::VALUES]
|
55
|
+
values = [values] if values.is_a?(String)
|
56
|
+
|
57
|
+
Kameleoon::CustomData.new(index, *values)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kameleoon/kameleoon_client_factory'
|
4
|
+
require 'kameleoon/resolver'
|
5
|
+
require 'open_feature/sdk/provider/provider_metadata'
|
6
|
+
require 'open_feature/sdk/provider/resolution_details'
|
7
|
+
|
8
|
+
module Kameleoon
|
9
|
+
# The KameleoonProvider class integrates with the OpenFeature SDK to provide feature flag resolution.
|
10
|
+
class KameleoonProvider
|
11
|
+
Provider = OpenFeature::SDK::Provider
|
12
|
+
NAME = 'Kameleoon Provider'
|
13
|
+
|
14
|
+
attr_reader :client, :metadata, :ready_state
|
15
|
+
|
16
|
+
# Initializes a new instance of the KameleoonProvider.
|
17
|
+
#
|
18
|
+
# @param site_code [String] The site code for the Kameleoon client.
|
19
|
+
# @param config [KameleoonClientConfig, nil] Optional configuration for the Kameleoon client.
|
20
|
+
# @param config_path [String, nil] Optional path to the configuration file.
|
21
|
+
def initialize(site_code, config: nil, config_path: nil)
|
22
|
+
@ready_state = false
|
23
|
+
@site_code = site_code
|
24
|
+
make_kameleoon_client(site_code, config, config_path)
|
25
|
+
@resolver = KameleoonResolver.new(@client)
|
26
|
+
@metadata = OpenFeature::SDK::Provider::ProviderMetadata.new(name: NAME).freeze
|
27
|
+
end
|
28
|
+
|
29
|
+
# Creates a Kameleoon client with the given site code and configuration.
|
30
|
+
#
|
31
|
+
# @param site_code [String] The site code for the Kameleoon client.
|
32
|
+
# @param config [KameleoonClientConfig, nil] Optional configuration for the Kameleoon client.
|
33
|
+
# @param config_path [String, nil] Optional path to the configuration file.
|
34
|
+
# @return [void]
|
35
|
+
private def make_kameleoon_client(site_code, config, config_path)
|
36
|
+
begin
|
37
|
+
@client = KameleoonClientFactory.create(site_code, config: config, config_path: config_path)
|
38
|
+
rescue Kameleoon::Exception::KameleoonError => exception
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Fetches a boolean value for the given feature flag.
|
44
|
+
#
|
45
|
+
# @param flag_key [String] The key of the feature flag.
|
46
|
+
# @param default_value [Boolean] The default value to return if the flag is not found.
|
47
|
+
# @param evaluation_context [EvaluationContext, nil] The evaluation context.
|
48
|
+
# @return [ResolutionDetails] The resolution details.
|
49
|
+
def fetch_boolean_value(flag_key: String, default_value:, evaluation_context: nil)
|
50
|
+
fetch_value(allowed_classes: [TrueClass, FalseClass], flag_key: flag_key, default_value: default_value, evaluation_context: evaluation_context)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Fetches a string value for the given feature flag.
|
54
|
+
#
|
55
|
+
# @param flag_key [String] The key of the feature flag.
|
56
|
+
# @param default_value [String] The default value to return if the flag is not found.
|
57
|
+
# @param evaluation_context [EvaluationContext, nil] The evaluation context.
|
58
|
+
# @return [ResolutionDetails] The resolution details.
|
59
|
+
def fetch_string_value(flag_key: String, default_value: String, evaluation_context: nil)
|
60
|
+
fetch_value(allowed_classes: [String], flag_key: flag_key, default_value: default_value, evaluation_context: evaluation_context)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Fetches a numeric value for the given feature flag.
|
64
|
+
#
|
65
|
+
# @param flag_key [String] The key of the feature flag.
|
66
|
+
# @param default_value [Numeric] The default value to return if the flag is not found.
|
67
|
+
# @param evaluation_context [EvaluationContext, nil] The evaluation context.
|
68
|
+
# @return [ResolutionDetails] The resolution details.
|
69
|
+
def fetch_number_value(flag_key: String, default_value: Numeric, evaluation_context: nil)
|
70
|
+
fetch_value(allowed_classes: [Numeric], flag_key: flag_key, default_value: default_value, evaluation_context: evaluation_context)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Fetches an integer value for the given feature flag.
|
74
|
+
#
|
75
|
+
# @param flag_key [String] The key of the feature flag.
|
76
|
+
# @param default_value [Integer] The default value to return if the flag is not found.
|
77
|
+
# @param evaluation_context [EvaluationContext, nil] The evaluation context.
|
78
|
+
# @return [ResolutionDetails] The resolution details.
|
79
|
+
def fetch_integer_value(flag_key: String, default_value: Integer, evaluation_context: nil)
|
80
|
+
fetch_value(allowed_classes: [Integer], flag_key: flag_key, default_value: default_value, evaluation_context: evaluation_context)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Fetches a float value for the given feature flag.
|
84
|
+
#
|
85
|
+
# @param flag_key [String] The key of the feature flag.
|
86
|
+
# @param default_value [Float] The default value to return if the flag is not found.
|
87
|
+
# @param evaluation_context [EvaluationContext, nil] The evaluation context.
|
88
|
+
# @return [ResolutionDetails] The resolution details.
|
89
|
+
def fetch_float_value(flag_key: String, default_value: Float, evaluation_context: nil)
|
90
|
+
fetch_value(allowed_classes: [Float], flag_key: flag_key, default_value: default_value, evaluation_context: evaluation_context)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Fetches an object value for the given feature flag.
|
94
|
+
#
|
95
|
+
# @param flag_key [String] The key of the feature flag.
|
96
|
+
# @param default_value [Array, Hash] The default value to return if the flag is not found.
|
97
|
+
# @param evaluation_context [EvaluationContext, nil] The evaluation context.
|
98
|
+
# @return [ResolutionDetails] The resolution details.
|
99
|
+
def fetch_object_value(flag_key: String, default_value:, evaluation_context: nil)
|
100
|
+
fetch_value(allowed_classes: [Array, Hash], flag_key: flag_key, default_value: default_value, evaluation_context: evaluation_context)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Initializes the Kameleoon client and sets the ready state.
|
104
|
+
#
|
105
|
+
# @return [Boolean]
|
106
|
+
def init
|
107
|
+
begin
|
108
|
+
success = @client.nil? ? false : @client.wait_init
|
109
|
+
rescue StandardError => e
|
110
|
+
success = false
|
111
|
+
end
|
112
|
+
@ready_state = success
|
113
|
+
end
|
114
|
+
|
115
|
+
# Shuts down the Kameleoon client and resets the ready state.
|
116
|
+
#
|
117
|
+
# @return [void]
|
118
|
+
def shutdown
|
119
|
+
KameleoonClientFactory.forget(@site_code)
|
120
|
+
@ready_state = false
|
121
|
+
@client = nil
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
# Fetches a value for the given feature flag based on the allowed classes.
|
127
|
+
#
|
128
|
+
# @param allowed_classes [Array<Class>] The allowed classes for the value.
|
129
|
+
# @param flag_key [String] The key of the feature flag.
|
130
|
+
# @param default_value [Any] The default value to return if the flag is not found.
|
131
|
+
# @param evaluation_context [EvaluationContext, nil] The evaluation context.
|
132
|
+
# @return [ResolutionDetails] The resolution details.
|
133
|
+
def fetch_value(allowed_classes:, flag_key:, default_value:, evaluation_context:)
|
134
|
+
if @ready_state
|
135
|
+
@resolver.resolve(allowed_classes: allowed_classes, flag_key: flag_key, default_value: default_value,
|
136
|
+
evaluation_context: evaluation_context)
|
137
|
+
else
|
138
|
+
Provider::ResolutionDetails.new(
|
139
|
+
value: default_value,
|
140
|
+
error_code: @client.nil? ? Provider::ErrorCode::PROVIDER_FATAL : Provider::ErrorCode::PROVIDER_NOT_READY,
|
141
|
+
error_message: 'The provider is not ready to resolve flags.',
|
142
|
+
reason: Provider::Reason::ERROR)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open_feature/sdk/provider/error_code'
|
4
|
+
require 'open_feature/sdk/provider/resolution_details'
|
5
|
+
require 'open_feature/sdk/provider/reason'
|
6
|
+
require 'open_feature/sdk/provider'
|
7
|
+
require 'kameleoon/data_converter'
|
8
|
+
|
9
|
+
module Kameleoon
|
10
|
+
VARIABLE_KEY = 'variableKey'
|
11
|
+
|
12
|
+
# Resolver interface which contains method for evaluations based on provided data
|
13
|
+
class Resolver
|
14
|
+
def resolve(flag: String, default_value:, evaluation_context: OpenFeature::SDK::EvaluationContext)
|
15
|
+
raise NotImplementedError, 'Subclasses must implement the resolve method'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# KameleoonResolver makes evaluations based on provided data, conforms to Resolver interface
|
20
|
+
class KameleoonResolver < Resolver
|
21
|
+
Provider = OpenFeature::SDK::Provider
|
22
|
+
|
23
|
+
def initialize(client)
|
24
|
+
@client = client
|
25
|
+
end
|
26
|
+
|
27
|
+
# Main method for getting resolution details based on provided data.
|
28
|
+
def resolve(allowed_classes: Array, flag_key: String, default_value:, evaluation_context: nil)
|
29
|
+
if !evaluation_context.is_a?(OpenFeature::SDK::EvaluationContext) ||
|
30
|
+
(visitor_code = get_targeting_key(evaluation_context)).nil? || visitor_code.empty?
|
31
|
+
return make_resolution_error(
|
32
|
+
default_value,
|
33
|
+
Provider::ErrorCode::TARGETING_KEY_MISSING,
|
34
|
+
'The TargetingKey is required in context and cannot be omitted.')
|
35
|
+
end
|
36
|
+
|
37
|
+
# Add targeting data from context to KameleoonClient by visitor code
|
38
|
+
@client.add_data(visitor_code, *DataConverter.to_kameleoon(evaluation_context))
|
39
|
+
|
40
|
+
# Get a variation (main SDK method)
|
41
|
+
variation = @client.get_variation(visitor_code, flag_key)
|
42
|
+
|
43
|
+
# Get a variant (variation key)
|
44
|
+
variant = variation.key
|
45
|
+
|
46
|
+
# Get variableKey if it's provided in context or any first in variation.
|
47
|
+
# It's the responsibility of the client to have only one variable per variation if
|
48
|
+
# variableKey is not provided.
|
49
|
+
variable_key = get_variable_key(evaluation_context, variation.variables)
|
50
|
+
|
51
|
+
# Get value by variable key
|
52
|
+
value = variation.variables[variable_key]&.value
|
53
|
+
|
54
|
+
if value.nil? || variable_key.empty?
|
55
|
+
return make_resolution_error(
|
56
|
+
default_value,
|
57
|
+
Provider::ErrorCode::FLAG_NOT_FOUND,
|
58
|
+
make_error_description(variant, variable_key),
|
59
|
+
variant: variant)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Check if the variable value has a required type
|
63
|
+
if allowed_classes.any? { |klass| value.is_a?(klass) }
|
64
|
+
return Provider::ResolutionDetails.new(
|
65
|
+
value: value,
|
66
|
+
reason: Provider::Reason::STATIC,
|
67
|
+
variant: variant)
|
68
|
+
else
|
69
|
+
make_resolution_error(
|
70
|
+
default_value,
|
71
|
+
Provider::ErrorCode::TYPE_MISMATCH,
|
72
|
+
'The type of value received is different from the requested value.',
|
73
|
+
variant: variant)
|
74
|
+
end
|
75
|
+
rescue Kameleoon::Exception::FeatureError => e
|
76
|
+
make_resolution_error(default_value, Provider::ErrorCode::FLAG_NOT_FOUND, e.message)
|
77
|
+
rescue Kameleoon::Exception::VisitorCodeInvalid => e
|
78
|
+
make_resolution_error(default_value, Provider::ErrorCode::INVALID_CONTEXT, e.message)
|
79
|
+
rescue StandardError => e
|
80
|
+
make_resolution_error(default_value, Provider::ErrorCode::GENERAL, e.message, variant: variant)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Helper method to get the targeting key from the context.
|
86
|
+
def get_targeting_key(evaluation_context)
|
87
|
+
targeting_key = evaluation_context.targeting_key
|
88
|
+
targeting_key if targeting_key.is_a?(String)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Helper method to get the variable key from the context or variables map.
|
92
|
+
def get_variable_key(context, variables)
|
93
|
+
variable_key = context.field(VARIABLE_KEY)
|
94
|
+
variable_key = variables.keys.first if variable_key.nil? || variable_key.empty?
|
95
|
+
variable_key
|
96
|
+
end
|
97
|
+
|
98
|
+
# Helper method to generate an error description based on the variant and variable key.
|
99
|
+
def make_error_description(variant, variable_key)
|
100
|
+
if variable_key.nil? || variable_key.empty?
|
101
|
+
"The variation '#{variant}' has no variables"
|
102
|
+
else
|
103
|
+
"The value for provided variable key '#{variable_key}' isn't found in variation '#{variant}'"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Helper method to generate a resolution details with error.
|
108
|
+
def make_resolution_error(default_value, error_code, error_message, variant: nil)
|
109
|
+
Provider::ResolutionDetails.new(
|
110
|
+
value: default_value,
|
111
|
+
error_code: error_code,
|
112
|
+
error_message: error_message,
|
113
|
+
reason: Provider::Reason::ERROR,
|
114
|
+
variant: variant)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kameleoon
|
4
|
+
module Types
|
5
|
+
module Data
|
6
|
+
# Type is used to add different Kameleoon data types using
|
7
|
+
# OpenFeature::SDK::EvaluationContext.
|
8
|
+
module Type
|
9
|
+
CONVERSION = 'conversion'
|
10
|
+
CUSTOM_DATA = 'customData'
|
11
|
+
end
|
12
|
+
|
13
|
+
# CustomDataType is used to add Kameleoon::CustomData using
|
14
|
+
# OpenFeature::SDK::EvaluationContext.
|
15
|
+
module CustomDataType
|
16
|
+
INDEX = 'index'
|
17
|
+
VALUES = 'values'
|
18
|
+
end
|
19
|
+
|
20
|
+
# ConversionType is used to add Kameleoon::Conversion using
|
21
|
+
# OpenFeature::SDK::EvaluationContext.
|
22
|
+
module ConversionType
|
23
|
+
GOAL_ID = 'goalId'
|
24
|
+
REVENUE = 'revenue'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kameleoon-openfeature-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kameleoon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-10-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: openfeature-sdk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.4.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.4.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: kameleoon-client-ruby
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.4.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.4.0
|
41
|
+
description: Kameleoon OpenFeature provider for the Ruby SDK
|
42
|
+
email:
|
43
|
+
- sdk@kameleoon.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- README.md
|
49
|
+
- lib/kameleoon/data_converter.rb
|
50
|
+
- lib/kameleoon/kameleoon_provider.rb
|
51
|
+
- lib/kameleoon/resolver.rb
|
52
|
+
- lib/kameleoon/types.rb
|
53
|
+
- lib/kameleoon/util/version.rb
|
54
|
+
homepage: https://developers.kameleoon.com/ruby-sdk.html
|
55
|
+
licenses:
|
56
|
+
- GPL-3.0
|
57
|
+
metadata: {}
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubygems_version: 3.3.26
|
74
|
+
signing_key:
|
75
|
+
specification_version: 4
|
76
|
+
summary: Kameleoon OpenFeature Ruby
|
77
|
+
test_files: []
|