yggdrasil-engine 1.1.0-universal-java-17 → 1.2.0-universal-java-17

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68226a9563883914f0dafca48d0dfc67b2a9e93be8b69c660a326ae3da728616
4
- data.tar.gz: 0110e43f06bcfbab825d3c042a7b5ee2edd5cd92d71c6fdab7262038a2b7b776
3
+ metadata.gz: 743de4af671f1321c015f1c9c98b17d755d3addd017329f61840d80bc3e50ba5
4
+ data.tar.gz: 5fc1d28448949c4478d8e0d8cc9f34f1cf4ef8140e7a8fe7e74972a5dd32c84e
5
5
  SHA512:
6
- metadata.gz: 886325ce4e88efd712d4bf7ed2a539e0bb8b7ab1c6662d653961056546c5fe6f39481eb0cbab47fbe9c86b59962779123ab18da7bffb0e392cf8a4866711fbb5
7
- data.tar.gz: 9e89d843286930d08812680d51febf2e388ea75486de44449264ec1a0d19267e91d122f77c61b9edff327a35cb03ab1076dba8d743b8d0362c57fe51fef14269
6
+ metadata.gz: e0a2812e76491d84f81560200741c29a85b685b1c35f5d9ae64e9660890b6c908779feb654cbbb75d0f917634ffee8ea7cadc6726e88e3b402f0ecadcb70b45e
7
+ data.tar.gz: 8d0710fe87d9eab9e5379c84bd69ef053b7bb241e1cd243c133cb37951789eaae2efebaeb2fc8d5ad9cdae27edf919e87cc7dcd46374f54c169195cd06d72ebf
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,6 @@
1
1
  require 'ffi'
2
2
  require 'json'
3
+ require 'logger'
3
4
  require 'custom_strategy'
4
5
 
5
6
  TOGGLE_MISSING_RESPONSE = 'NotFound'.freeze
@@ -69,6 +70,21 @@ class YggdrasilEngine
69
70
 
70
71
  attach_function :list_known_toggles, [:pointer], :pointer
71
72
 
73
+ attach_function :define_counter, %i[pointer string string], :pointer
74
+ attach_function :inc_counter, %i[pointer string int64 string], :pointer
75
+ attach_function :collect_impact_metrics, [:pointer], :pointer
76
+ attach_function :restore_impact_metrics, %i[pointer string], :pointer
77
+ attach_function :define_gauge, %i[pointer string string], :pointer
78
+ attach_function :set_gauge, %i[pointer string double string], :pointer
79
+ attach_function :define_histogram, %i[pointer string string string], :pointer
80
+ attach_function :observe_histogram, %i[pointer string double string], :pointer
81
+
82
+ class << self
83
+ attr_accessor :logger
84
+ end
85
+
86
+ self.logger = Logger.new($stderr, level: Logger::WARN)
87
+
72
88
  def initialize
73
89
  @engine = YggdrasilEngine.new_engine
74
90
  @custom_strategy_handler = CustomStrategyHandler.new
@@ -83,6 +99,9 @@ class YggdrasilEngine
83
99
  @custom_strategy_handler.update_strategies(toggles)
84
100
  response_ptr = YggdrasilEngine.take_state(@engine, toggles)
85
101
  take_toggles_response = JSON.parse(response_ptr.read_string, symbolize_names: true)
102
+ if take_toggles_response[:status_code] == ERROR_RESPONSE
103
+ self.class.logger.error("Error taking state, flags were not updated: #{take_toggles_response[:error_message]}")
104
+ end
86
105
  YggdrasilEngine.free_response(response_ptr)
87
106
  end
88
107
 
@@ -93,7 +112,7 @@ class YggdrasilEngine
93
112
  response = JSON.parse(response_json, symbolize_names: true)
94
113
 
95
114
  raise "Error: #{response[:error_message]}" if response[:status_code] == ERROR_RESPONSE
96
-
115
+
97
116
  response[:value].to_json
98
117
  ensure
99
118
  YggdrasilEngine.free_response(response_ptr)
@@ -157,4 +176,67 @@ class YggdrasilEngine
157
176
  def register_custom_strategies(strategies)
158
177
  @custom_strategy_handler.register_custom_strategies(strategies)
159
178
  end
179
+
180
+ def define_counter(name, help_text)
181
+ response_ptr = YggdrasilEngine.define_counter(@engine, name, help_text)
182
+ handle_response(response_ptr)
183
+ end
184
+
185
+ def inc_counter(name, value = 1, labels = nil)
186
+ labels_json = labels&.to_json
187
+ response_ptr = YggdrasilEngine.inc_counter(@engine, name, value, labels_json)
188
+ handle_response(response_ptr)
189
+ end
190
+
191
+ def collect_impact_metrics
192
+ response_ptr = YggdrasilEngine.collect_impact_metrics(@engine)
193
+ response_json = response_ptr.read_string
194
+ YggdrasilEngine.free_response(response_ptr)
195
+ response = JSON.parse(response_json, symbolize_names: true)
196
+
197
+ raise "Error: #{response[:error_message]}" if response[:status_code] == ERROR_RESPONSE
198
+
199
+ response[:value] || []
200
+ end
201
+
202
+ def restore_impact_metrics(metrics)
203
+ metrics_json = metrics.to_json
204
+ response_ptr = YggdrasilEngine.restore_impact_metrics(@engine, metrics_json)
205
+ handle_response(response_ptr)
206
+ end
207
+
208
+ def define_gauge(name, help_text)
209
+ response_ptr = YggdrasilEngine.define_gauge(@engine, name, help_text)
210
+ handle_response(response_ptr)
211
+ end
212
+
213
+ def set_gauge(name, value, labels = nil)
214
+ labels_json = labels&.to_json
215
+ response_ptr = YggdrasilEngine.set_gauge(@engine, name, value, labels_json)
216
+ handle_response(response_ptr)
217
+ end
218
+
219
+ def define_histogram(name, help_text, buckets = nil)
220
+ buckets_json = (buckets || []).to_json
221
+ response_ptr = YggdrasilEngine.define_histogram(@engine, name, help_text, buckets_json)
222
+ handle_response(response_ptr)
223
+ end
224
+
225
+ def observe_histogram(name, value, labels = nil)
226
+ labels_json = labels&.to_json
227
+ response_ptr = YggdrasilEngine.observe_histogram(@engine, name, value, labels_json)
228
+ handle_response(response_ptr)
229
+ end
230
+
231
+ private
232
+
233
+ def handle_response(response_ptr)
234
+ response_json = response_ptr.read_string
235
+ YggdrasilEngine.free_response(response_ptr)
236
+ response = JSON.parse(response_json, symbolize_names: true)
237
+
238
+ raise "Error: #{response[:error_message]}" if response[:status_code] == ERROR_RESPONSE
239
+
240
+ nil
241
+ end
160
242
  end
Binary file
Binary file
Binary file
@@ -0,0 +1,166 @@
1
+ require 'rspec'
2
+ require 'json'
3
+ require_relative '../lib/yggdrasil_engine'
4
+
5
+ RSpec.describe YggdrasilEngine do
6
+ let(:yggdrasil_engine) { YggdrasilEngine.new }
7
+
8
+ describe '#inc_counter' do
9
+ it 'should increment counter value' do
10
+ yggdrasil_engine.define_counter('test_counter', 'Test counter')
11
+ yggdrasil_engine.inc_counter('test_counter', 5)
12
+ yggdrasil_engine.inc_counter('test_counter', 3)
13
+
14
+ metrics = yggdrasil_engine.collect_impact_metrics()
15
+ counter = metrics.find { |m| m[:name] == 'test_counter' }
16
+
17
+ expect(counter).not_to be_nil
18
+ expect(counter[:help]).to eq('Test counter')
19
+ expect(counter[:samples].length).to eq(1)
20
+ expect(counter[:samples][0][:value]).to eq(8)
21
+ end
22
+
23
+ it 'should increment counter with labels' do
24
+ yggdrasil_engine.define_counter('test_counter', 'Test counter')
25
+ yggdrasil_engine.inc_counter('test_counter', 5, { 'env' => 'test' })
26
+ yggdrasil_engine.inc_counter('test_counter', 3, { 'env' => 'prod' })
27
+
28
+ metrics = yggdrasil_engine.collect_impact_metrics()
29
+ counter = metrics.find { |m| m[:name] == 'test_counter' }
30
+
31
+ expect(counter).not_to be_nil
32
+ expect(counter[:samples].length).to eq(2)
33
+
34
+ test_sample = counter[:samples].find { |s| s[:labels][:env] == 'test' }
35
+ prod_sample = counter[:samples].find { |s| s[:labels][:env] == 'prod' }
36
+
37
+ expect(test_sample[:value]).to eq(5)
38
+ expect(prod_sample[:value]).to eq(3)
39
+ end
40
+ end
41
+
42
+ describe '#set_gauge' do
43
+ it 'should set gauge value' do
44
+ yggdrasil_engine.define_gauge('test_gauge', 'Test gauge')
45
+ yggdrasil_engine.set_gauge('test_gauge', 5.5)
46
+ yggdrasil_engine.set_gauge('test_gauge', 10.7)
47
+
48
+ metrics = yggdrasil_engine.collect_impact_metrics()
49
+ gauge = metrics.find { |m| m[:name] == 'test_gauge' }
50
+
51
+ expect(gauge).not_to be_nil
52
+ expect(gauge[:help]).to eq('Test gauge')
53
+ expect(gauge[:samples].length).to eq(1)
54
+ expect(gauge[:samples][0][:value]).to eq(10.7)
55
+ end
56
+
57
+ it 'should set gauge with labels' do
58
+ yggdrasil_engine.define_gauge('test_gauge', 'Test gauge')
59
+ yggdrasil_engine.set_gauge('test_gauge', 5.25, { 'env' => 'test' })
60
+ yggdrasil_engine.set_gauge('test_gauge', 3.14, { 'env' => 'prod' })
61
+
62
+ metrics = yggdrasil_engine.collect_impact_metrics()
63
+ gauge = metrics.find { |m| m[:name] == 'test_gauge' }
64
+
65
+ expect(gauge).not_to be_nil
66
+ expect(gauge[:samples].length).to eq(2)
67
+
68
+ test_sample = gauge[:samples].find { |s| s[:labels][:env] == 'test' }
69
+ prod_sample = gauge[:samples].find { |s| s[:labels][:env] == 'prod' }
70
+
71
+ expect(test_sample[:value]).to eq(5.25)
72
+ expect(prod_sample[:value]).to eq(3.14)
73
+ end
74
+ end
75
+
76
+ describe '#observe_histogram' do
77
+ it 'should observe histogram values' do
78
+ yggdrasil_engine.define_histogram('request_duration', 'Request duration', [0.1, 0.5, 1.0, 5.0])
79
+ yggdrasil_engine.observe_histogram('request_duration', 0.05)
80
+ yggdrasil_engine.observe_histogram('request_duration', 0.75)
81
+ yggdrasil_engine.observe_histogram('request_duration', 3.0)
82
+
83
+ metrics = yggdrasil_engine.collect_impact_metrics()
84
+ histogram = metrics.find { |m| m[:name] == 'request_duration' }
85
+
86
+ expect(histogram).not_to be_nil
87
+ expect(histogram[:help]).to eq('Request duration')
88
+ expect(histogram[:type]).to eq('histogram')
89
+ expect(histogram[:samples].length).to eq(1)
90
+ end
91
+
92
+ it 'should observe histogram with labels' do
93
+ yggdrasil_engine.define_histogram('request_duration', 'Request duration', [0.1, 0.5, 1.0, 5.0])
94
+ yggdrasil_engine.observe_histogram('request_duration', 0.05, { 'env' => 'test' })
95
+ yggdrasil_engine.observe_histogram('request_duration', 0.75, { 'env' => 'prod' })
96
+
97
+ metrics = yggdrasil_engine.collect_impact_metrics()
98
+ histogram = metrics.find { |m| m[:name] == 'request_duration' }
99
+
100
+ expect(histogram).not_to be_nil
101
+ expect(histogram[:samples].length).to eq(2)
102
+
103
+ test_sample = histogram[:samples].find { |s| s[:labels][:env] == 'test' }
104
+ prod_sample = histogram[:samples].find { |s| s[:labels][:env] == 'prod' }
105
+
106
+ expect(test_sample).not_to be_nil
107
+ expect(prod_sample).not_to be_nil
108
+ end
109
+ end
110
+
111
+ describe '#define_histogram' do
112
+ it 'should define histogram with default buckets' do
113
+ yggdrasil_engine.define_histogram('request_duration', 'Request duration')
114
+ yggdrasil_engine.observe_histogram('request_duration', 0.05)
115
+
116
+ metrics = yggdrasil_engine.collect_impact_metrics()
117
+ histogram = metrics.find { |m| m[:name] == 'request_duration' }
118
+
119
+ expect(histogram).not_to be_nil
120
+ expect(histogram[:type]).to eq('histogram')
121
+ expect(histogram[:samples].length).to eq(1)
122
+ end
123
+ end
124
+
125
+ describe '#collect_impact_metrics' do
126
+ it 'should return empty list when no metrics' do
127
+ metrics = yggdrasil_engine.collect_impact_metrics()
128
+ expect(metrics).to eq([])
129
+ end
130
+ end
131
+
132
+ describe '#restore_impact_metrics' do
133
+ it 'should restore impact metrics' do
134
+ yggdrasil_engine.define_counter('test_counter', 'Test counter')
135
+ yggdrasil_engine.inc_counter('test_counter', 10)
136
+ yggdrasil_engine.define_gauge('test_gauge', 'Test gauge')
137
+ yggdrasil_engine.set_gauge('test_gauge', 42)
138
+ yggdrasil_engine.define_histogram('test_histogram', 'Test histogram', [0.1, 0.5, 1.0])
139
+ yggdrasil_engine.observe_histogram('test_histogram', 0.25)
140
+
141
+ metrics = yggdrasil_engine.collect_impact_metrics()
142
+ expect(metrics.length).to eq(3)
143
+
144
+ counter = metrics.find { |m| m[:name] == 'test_counter' }
145
+ gauge = metrics.find { |m| m[:name] == 'test_gauge' }
146
+ histogram = metrics.find { |m| m[:name] == 'test_histogram' }
147
+
148
+ expect(counter[:samples][0][:value]).to eq(10)
149
+ expect(gauge[:samples][0][:value]).to eq(42)
150
+ expect(histogram).not_to be_nil
151
+
152
+ yggdrasil_engine.restore_impact_metrics(metrics)
153
+
154
+ restored_metrics = yggdrasil_engine.collect_impact_metrics()
155
+ expect(restored_metrics.length).to eq(3)
156
+
157
+ restored_counter = restored_metrics.find { |m| m[:name] == 'test_counter' }
158
+ restored_gauge = restored_metrics.find { |m| m[:name] == 'test_gauge' }
159
+ restored_histogram = restored_metrics.find { |m| m[:name] == 'test_histogram' }
160
+
161
+ expect(restored_counter[:samples][0][:value]).to eq(10)
162
+ expect(restored_gauge[:samples][0][:value]).to eq(42)
163
+ expect(restored_histogram).not_to be_nil
164
+ end
165
+ end
166
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yggdrasil-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: universal-java-17
6
6
  authors:
7
7
  - Unleash
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2023-06-28 00:00:00.000000000 Z
10
+ date: 2026-01-23 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ffi
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 1.16.3
18
+ version: 1.17.2
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 1.16.3
25
+ version: 1.17.2
26
26
  description: "..."
27
27
  email: liquidwicked64@gmail.com
28
28
  executables: []
@@ -39,14 +39,16 @@ files:
39
39
  - lib/libyggdrasilffi_x86_64.so
40
40
  - lib/yggdrasil_engine.rb
41
41
  - lib/yggdrasilffi_arm64.dll
42
+ - lib/yggdrasilffi_i686.dll
42
43
  - lib/yggdrasilffi_x86_64.dll
43
44
  - spec/custom_strategy_spec.rb
45
+ - spec/impact_metrics_spec.rb
44
46
  - spec/yggdrasil_engine_spec.rb
45
- homepage: http://github.com/username/my_gem
47
+ homepage: https://github.com/Unleash/yggdrasil-bindings
46
48
  licenses:
47
49
  - MIT
48
50
  metadata:
49
- yggdrasil_core_version: 0.18.1
51
+ yggdrasil_core_version: 0.20.0
50
52
  rdoc_options: []
51
53
  require_paths:
52
54
  - lib