togglr-sdk 1.0.5 → 1.1.0

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: ff652d531c0ed02ec75cf399743544e157fdd142a9ce2000847b0d1ba4d855cc
4
- data.tar.gz: f90b18e8a8be739e557e6d21b6d2dda227dadcd417828374ba33e26b1ea9f5c4
3
+ metadata.gz: dfbbf2759ec78ebca965b0a80d91cbd85a438f482d2c942d02345b9ad8bb51ee
4
+ data.tar.gz: 5ae06a50fb62a913ed453c3924f6a6921dc3788bd932052bbd723c940975d293
5
5
  SHA512:
6
- metadata.gz: d802ffa645e880e88d67c69c08346f9ce27a2547a0ed5ba8ec717ea9c29e58f034906ea5e29c73b41ba50e3fcc73d1e220f9525464dbb6b491b56412a1aa8397
7
- data.tar.gz: f0b93fc6b700bc6b26a42b6295bb4daf8bf2138b08af990ea0ae4142d6cdcaf6b7b22ef384e9ac7642475fe9b39d43b20222cab489dd14f7c471c90e334425a1
6
+ metadata.gz: ce36f78850636242c0f88c3d00213628e562f111c483d0ede4ef1b6ae64332caca90dcda0894ba019ce18e32438931a73b24a21f8e5e1ed1733ac974c9c61622
7
+ data.tar.gz: fe7f9725f634425c6e09f8e5938263b35c4730f9099201c1a267212e154baf3f6e5d65fa82d0945bcf3f8c27c8ae6e297a54c1a581c7c088fcce0a7bbbe9e5e1
data/lib/togglr/client.rb CHANGED
@@ -125,6 +125,14 @@ module Togglr
125
125
  health.healthy?
126
126
  end
127
127
 
128
+ # Track an event for analytics
129
+ def track_event(feature_key, event)
130
+ error = track_event_with_retries(feature_key, event)
131
+ raise error if error
132
+
133
+ nil # Success - event queued for processing
134
+ end
135
+
128
136
  def close
129
137
  @cache&.clear
130
138
  end
@@ -260,5 +268,35 @@ module Togglr
260
268
  'last_error_at' => api_health.last_error_at
261
269
  })
262
270
  end
271
+
272
+ def track_event_with_retries(feature_key, event)
273
+ with_retries(max_tries: @config.retries + 1) do
274
+ error = track_event_single(feature_key, event)
275
+ raise error if error
276
+
277
+ nil # Success
278
+ end
279
+ end
280
+
281
+ def track_event_single(feature_key, event)
282
+ track_request = TogglrClient::TrackRequest.new(event.to_h)
283
+
284
+ @api_client.track_feature_event(feature_key, track_request)
285
+ # Success - event queued for processing
286
+ nil
287
+ rescue TogglrClient::ApiError => e
288
+ case e.code
289
+ when 401
290
+ UnauthorizedError.new('Authentication required')
291
+ when 400
292
+ BadRequestError.new('Bad request')
293
+ when 404
294
+ FeatureNotFoundError.new("Feature #{feature_key} not found")
295
+ when 500
296
+ InternalServerError.new('Internal server error')
297
+ else
298
+ APIError.new(e.code.to_s, e.message, e.code)
299
+ end
300
+ end
263
301
  end
264
302
  end
@@ -0,0 +1,80 @@
1
+ module Togglr
2
+ class TrackEvent
3
+ # Event types
4
+ SUCCESS = 'success'.freeze
5
+ FAILURE = 'failure'.freeze
6
+ ERROR = 'error'.freeze
7
+
8
+ attr_reader :variant_key, :event_type, :reward, :context, :created_at, :dedup_key
9
+
10
+ def initialize(variant_key, event_type, options = {})
11
+ @variant_key = variant_key
12
+ @event_type = event_type
13
+ @reward = options[:reward]
14
+ @context = options[:context] || RequestContext.new
15
+ @created_at = options[:created_at]
16
+ @dedup_key = options[:dedup_key]
17
+ end
18
+
19
+ def self.new(variant_key, event_type, options = {})
20
+ instance = allocate
21
+ instance.send(:initialize, variant_key, event_type, options)
22
+ instance
23
+ end
24
+
25
+ def with_reward(reward)
26
+ @reward = reward
27
+ self
28
+ end
29
+
30
+ def with_context(key, value)
31
+ @context.set(key, value)
32
+ self
33
+ end
34
+
35
+ def with_contexts(contexts)
36
+ contexts.each { |key, value| @context.set(key, value) }
37
+ self
38
+ end
39
+
40
+ def with_request_context(context)
41
+ @context = context
42
+ self
43
+ end
44
+
45
+ def with_created_at(created_at)
46
+ @created_at = created_at
47
+ self
48
+ end
49
+
50
+ def with_dedup_key(dedup_key)
51
+ @dedup_key = dedup_key
52
+ self
53
+ end
54
+
55
+ def to_h
56
+ result = {
57
+ 'variant_key' => @variant_key,
58
+ 'event_type' => @event_type,
59
+ 'context' => @context.to_h
60
+ }
61
+
62
+ result['reward'] = @reward if @reward
63
+ result['created_at'] = @created_at.iso8601 if @created_at
64
+ result['dedup_key'] = @dedup_key if @dedup_key
65
+
66
+ result
67
+ end
68
+
69
+ def to_s
70
+ "#<Togglr::TrackEvent variant_key=#{@variant_key.inspect} " \
71
+ "event_type=#{@event_type.inspect} reward=#{@reward.inspect} " \
72
+ "context=#{@context.inspect} created_at=#{@created_at.inspect} " \
73
+ "dedup_key=#{@dedup_key.inspect}>"
74
+ end
75
+
76
+ def inspect
77
+ to_s
78
+ end
79
+ end
80
+ end
@@ -1,3 +1,3 @@
1
1
  module Togglr
2
- VERSION = '1.0.5'.freeze
2
+ VERSION = '1.1.0'.freeze
3
3
  end
@@ -280,5 +280,79 @@ module TogglrClient
280
280
  end
281
281
  return data, status_code, headers
282
282
  end
283
+
284
+ # Track event for a feature (impression / conversion / error / custom)
285
+ # Send a feedback event related to a feature evaluation. Events are written to TimescaleDB (hypertable) and used for analytics, auto-disable and training MAB algorithms. The project is derived from the API key.
286
+ # @param feature_key [String]
287
+ # @param track_request [TrackRequest]
288
+ # @param [Hash] opts the optional parameters
289
+ # @return [nil]
290
+ def track_feature_event(feature_key, track_request, opts = {})
291
+ track_feature_event_with_http_info(feature_key, track_request, opts)
292
+ nil
293
+ end
294
+
295
+ # Track event for a feature (impression / conversion / error / custom)
296
+ # Send a feedback event related to a feature evaluation. Events are written to TimescaleDB (hypertable) and used for analytics, auto-disable and training MAB algorithms. The project is derived from the API key.
297
+ # @param feature_key [String]
298
+ # @param track_request [TrackRequest]
299
+ # @param [Hash] opts the optional parameters
300
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
301
+ def track_feature_event_with_http_info(feature_key, track_request, opts = {})
302
+ if @api_client.config.debugging
303
+ @api_client.config.logger.debug 'Calling API: DefaultApi.track_feature_event ...'
304
+ end
305
+ # verify the required parameter 'feature_key' is set
306
+ if @api_client.config.client_side_validation && feature_key.nil?
307
+ fail ArgumentError, "Missing the required parameter 'feature_key' when calling DefaultApi.track_feature_event"
308
+ end
309
+ # verify the required parameter 'track_request' is set
310
+ if @api_client.config.client_side_validation && track_request.nil?
311
+ fail ArgumentError, "Missing the required parameter 'track_request' when calling DefaultApi.track_feature_event"
312
+ end
313
+ # resource path
314
+ local_var_path = '/sdk/v1/features/{feature_key}/track'.sub('{' + 'feature_key' + '}', CGI.escape(feature_key.to_s))
315
+
316
+ # query parameters
317
+ query_params = opts[:query_params] || {}
318
+
319
+ # header parameters
320
+ header_params = opts[:header_params] || {}
321
+ # HTTP header 'Accept' (if needed)
322
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
323
+ # HTTP header 'Content-Type'
324
+ content_type = @api_client.select_header_content_type(['application/json'])
325
+ if !content_type.nil?
326
+ header_params['Content-Type'] = content_type
327
+ end
328
+
329
+ # form parameters
330
+ form_params = opts[:form_params] || {}
331
+
332
+ # http body (model)
333
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(track_request)
334
+
335
+ # return_type
336
+ return_type = opts[:debug_return_type]
337
+
338
+ # auth_names
339
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
340
+
341
+ new_options = opts.merge(
342
+ :operation => :"DefaultApi.track_feature_event",
343
+ :header_params => header_params,
344
+ :query_params => query_params,
345
+ :form_params => form_params,
346
+ :body => post_body,
347
+ :auth_names => auth_names,
348
+ :return_type => return_type
349
+ )
350
+
351
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
352
+ if @api_client.config.debugging
353
+ @api_client.config.logger.debug "API called: DefaultApi#track_feature_event\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
354
+ end
355
+ return data, status_code, headers
356
+ end
283
357
  end
284
358
  end
@@ -0,0 +1,332 @@
1
+ =begin
2
+ #SDK API
3
+
4
+ #No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.16.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module TogglrClient
17
+ # Event sent from SDK. SDK SHOULD send an impression event for each evaluation (recommended). Conversions / errors / custom events are used to update algorithm statistics.
18
+ class TrackRequest
19
+ # Variant key returned by evaluate (e.g. \"A\", \"v2\").
20
+ attr_accessor :variant_key
21
+
22
+ # Type of event (e.g. \"success\", \"failure\", \"error\").
23
+ attr_accessor :event_type
24
+
25
+ # Numeric reward associated with event (e.g. 1.0 for conversion). Default 0.
26
+ attr_accessor :reward
27
+
28
+ # Arbitrary context passed by SDK (user id, session, metadata).
29
+ attr_accessor :context
30
+
31
+ # Event timestamp. If omitted, server time will be used.
32
+ attr_accessor :created_at
33
+
34
+ # Optional idempotency key to deduplicate duplicate events from SDK retries.
35
+ attr_accessor :dedup_key
36
+
37
+ class EnumAttributeValidator
38
+ attr_reader :datatype
39
+ attr_reader :allowable_values
40
+
41
+ def initialize(datatype, allowable_values)
42
+ @allowable_values = allowable_values.map do |value|
43
+ case datatype.to_s
44
+ when /Integer/i
45
+ value.to_i
46
+ when /Float/i
47
+ value.to_f
48
+ else
49
+ value
50
+ end
51
+ end
52
+ end
53
+
54
+ def valid?(value)
55
+ !value || allowable_values.include?(value)
56
+ end
57
+ end
58
+
59
+ # Attribute mapping from ruby-style variable name to JSON key.
60
+ def self.attribute_map
61
+ {
62
+ :'variant_key' => :'variant_key',
63
+ :'event_type' => :'event_type',
64
+ :'reward' => :'reward',
65
+ :'context' => :'context',
66
+ :'created_at' => :'created_at',
67
+ :'dedup_key' => :'dedup_key'
68
+ }
69
+ end
70
+
71
+ # Returns attribute mapping this model knows about
72
+ def self.acceptable_attribute_map
73
+ attribute_map
74
+ end
75
+
76
+ # Returns all the JSON keys this model knows about
77
+ def self.acceptable_attributes
78
+ acceptable_attribute_map.values
79
+ end
80
+
81
+ # Attribute type mapping.
82
+ def self.openapi_types
83
+ {
84
+ :'variant_key' => :'String',
85
+ :'event_type' => :'String',
86
+ :'reward' => :'Float',
87
+ :'context' => :'Hash<String, Object>',
88
+ :'created_at' => :'Time',
89
+ :'dedup_key' => :'String'
90
+ }
91
+ end
92
+
93
+ # List of attributes with nullable: true
94
+ def self.openapi_nullable
95
+ Set.new([
96
+ ])
97
+ end
98
+
99
+ # Initializes the object
100
+ # @param [Hash] attributes Model attributes in the form of hash
101
+ def initialize(attributes = {})
102
+ if (!attributes.is_a?(Hash))
103
+ fail ArgumentError, "The input argument (attributes) must be a hash in `TogglrClient::TrackRequest` initialize method"
104
+ end
105
+
106
+ # check to see if the attribute exists and convert string to symbol for hash key
107
+ acceptable_attribute_map = self.class.acceptable_attribute_map
108
+ attributes = attributes.each_with_object({}) { |(k, v), h|
109
+ if (!acceptable_attribute_map.key?(k.to_sym))
110
+ fail ArgumentError, "`#{k}` is not a valid attribute in `TogglrClient::TrackRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
111
+ end
112
+ h[k.to_sym] = v
113
+ }
114
+
115
+ if attributes.key?(:'variant_key')
116
+ self.variant_key = attributes[:'variant_key']
117
+ else
118
+ self.variant_key = nil
119
+ end
120
+
121
+ if attributes.key?(:'event_type')
122
+ self.event_type = attributes[:'event_type']
123
+ else
124
+ self.event_type = nil
125
+ end
126
+
127
+ if attributes.key?(:'reward')
128
+ self.reward = attributes[:'reward']
129
+ end
130
+
131
+ if attributes.key?(:'context')
132
+ if (value = attributes[:'context']).is_a?(Hash)
133
+ self.context = value
134
+ end
135
+ end
136
+
137
+ if attributes.key?(:'created_at')
138
+ self.created_at = attributes[:'created_at']
139
+ end
140
+
141
+ if attributes.key?(:'dedup_key')
142
+ self.dedup_key = attributes[:'dedup_key']
143
+ end
144
+ end
145
+
146
+ # Show invalid properties with the reasons. Usually used together with valid?
147
+ # @return Array for valid properties with the reasons
148
+ def list_invalid_properties
149
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
150
+ invalid_properties = Array.new
151
+ if @variant_key.nil?
152
+ invalid_properties.push('invalid value for "variant_key", variant_key cannot be nil.')
153
+ end
154
+
155
+ if @event_type.nil?
156
+ invalid_properties.push('invalid value for "event_type", event_type cannot be nil.')
157
+ end
158
+
159
+ invalid_properties
160
+ end
161
+
162
+ # Check to see if the all the properties in the model are valid
163
+ # @return true if the model is valid
164
+ def valid?
165
+ warn '[DEPRECATED] the `valid?` method is obsolete'
166
+ return false if @variant_key.nil?
167
+ return false if @event_type.nil?
168
+ event_type_validator = EnumAttributeValidator.new('String', ["success", "failure", "error"])
169
+ return false unless event_type_validator.valid?(@event_type)
170
+ true
171
+ end
172
+
173
+ # Custom attribute writer method with validation
174
+ # @param [Object] variant_key Value to be assigned
175
+ def variant_key=(variant_key)
176
+ if variant_key.nil?
177
+ fail ArgumentError, 'variant_key cannot be nil'
178
+ end
179
+
180
+ @variant_key = variant_key
181
+ end
182
+
183
+ # Custom attribute writer method checking allowed values (enum).
184
+ # @param [Object] event_type Object to be assigned
185
+ def event_type=(event_type)
186
+ validator = EnumAttributeValidator.new('String', ["success", "failure", "error"])
187
+ unless validator.valid?(event_type)
188
+ fail ArgumentError, "invalid value for \"event_type\", must be one of #{validator.allowable_values}."
189
+ end
190
+ @event_type = event_type
191
+ end
192
+
193
+ # Checks equality by comparing each attribute.
194
+ # @param [Object] Object to be compared
195
+ def ==(o)
196
+ return true if self.equal?(o)
197
+ self.class == o.class &&
198
+ variant_key == o.variant_key &&
199
+ event_type == o.event_type &&
200
+ reward == o.reward &&
201
+ context == o.context &&
202
+ created_at == o.created_at &&
203
+ dedup_key == o.dedup_key
204
+ end
205
+
206
+ # @see the `==` method
207
+ # @param [Object] Object to be compared
208
+ def eql?(o)
209
+ self == o
210
+ end
211
+
212
+ # Calculates hash code according to all attributes.
213
+ # @return [Integer] Hash code
214
+ def hash
215
+ [variant_key, event_type, reward, context, created_at, dedup_key].hash
216
+ end
217
+
218
+ # Builds the object from hash
219
+ # @param [Hash] attributes Model attributes in the form of hash
220
+ # @return [Object] Returns the model itself
221
+ def self.build_from_hash(attributes)
222
+ return nil unless attributes.is_a?(Hash)
223
+ attributes = attributes.transform_keys(&:to_sym)
224
+ transformed_hash = {}
225
+ openapi_types.each_pair do |key, type|
226
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
227
+ transformed_hash["#{key}"] = nil
228
+ elsif type =~ /\AArray<(.*)>/i
229
+ # check to ensure the input is an array given that the attribute
230
+ # is documented as an array but the input is not
231
+ if attributes[attribute_map[key]].is_a?(Array)
232
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
233
+ end
234
+ elsif !attributes[attribute_map[key]].nil?
235
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
236
+ end
237
+ end
238
+ new(transformed_hash)
239
+ end
240
+
241
+ # Deserializes the data based on type
242
+ # @param string type Data type
243
+ # @param string value Value to be deserialized
244
+ # @return [Object] Deserialized data
245
+ def self._deserialize(type, value)
246
+ case type.to_sym
247
+ when :Time
248
+ Time.parse(value)
249
+ when :Date
250
+ Date.parse(value)
251
+ when :String
252
+ value.to_s
253
+ when :Integer
254
+ value.to_i
255
+ when :Float
256
+ value.to_f
257
+ when :Boolean
258
+ if value.to_s =~ /\A(true|t|yes|y|1)\z/i
259
+ true
260
+ else
261
+ false
262
+ end
263
+ when :Object
264
+ # generic object (usually a Hash), return directly
265
+ value
266
+ when /\AArray<(?<inner_type>.+)>\z/
267
+ inner_type = Regexp.last_match[:inner_type]
268
+ value.map { |v| _deserialize(inner_type, v) }
269
+ when /\AHash<(?<k_type>.+?), (?<v_type>.+)>\z/
270
+ k_type = Regexp.last_match[:k_type]
271
+ v_type = Regexp.last_match[:v_type]
272
+ {}.tap do |hash|
273
+ value.each do |k, v|
274
+ hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
275
+ end
276
+ end
277
+ else # model
278
+ # models (e.g. Pet) or oneOf
279
+ klass = TogglrClient.const_get(type)
280
+ klass.respond_to?(:openapi_any_of) || klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value)
281
+ end
282
+ end
283
+
284
+ # Returns the string representation of the object
285
+ # @return [String] String presentation of the object
286
+ def to_s
287
+ to_hash.to_s
288
+ end
289
+
290
+ # to_body is an alias to to_hash (backward compatibility)
291
+ # @return [Hash] Returns the object in the form of hash
292
+ def to_body
293
+ to_hash
294
+ end
295
+
296
+ # Returns the object in the form of hash
297
+ # @return [Hash] Returns the object in the form of hash
298
+ def to_hash
299
+ hash = {}
300
+ self.class.attribute_map.each_pair do |attr, param|
301
+ value = self.send(attr)
302
+ if value.nil?
303
+ is_nullable = self.class.openapi_nullable.include?(attr)
304
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
305
+ end
306
+
307
+ hash[param] = _to_hash(value)
308
+ end
309
+ hash
310
+ end
311
+
312
+ # Outputs non-array value in the form of hash
313
+ # For object, use to_hash. Otherwise, just return the value
314
+ # @param [Object] value Any valid value
315
+ # @return [Hash] Returns the value in the form of hash
316
+ def _to_hash(value)
317
+ if value.is_a?(Array)
318
+ value.compact.map { |v| _to_hash(v) }
319
+ elsif value.is_a?(Hash)
320
+ {}.tap do |hash|
321
+ value.each { |k, v| hash[k] = _to_hash(v) }
322
+ end
323
+ elsif value.respond_to? :to_hash
324
+ value.to_hash
325
+ else
326
+ value
327
+ end
328
+ end
329
+
330
+ end
331
+
332
+ end
data/lib/togglr-client.rb CHANGED
@@ -29,6 +29,7 @@ require 'togglr-client/models/evaluate_response'
29
29
  require 'togglr-client/models/feature_error_report'
30
30
  require 'togglr-client/models/feature_health'
31
31
  require 'togglr-client/models/health_response'
32
+ require 'togglr-client/models/track_request'
32
33
 
33
34
  # APIs
34
35
  require 'togglr-client/api/default_api'
data/lib/togglr.rb CHANGED
@@ -2,6 +2,7 @@ require_relative 'togglr/version'
2
2
  require_relative 'togglr/config'
3
3
  require_relative 'togglr/client'
4
4
  require_relative 'togglr/request_context'
5
+ require_relative 'togglr/track_event'
5
6
  require_relative 'togglr/errors'
6
7
  require_relative 'togglr/cache'
7
8
  require_relative 'togglr/logger'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: togglr-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-09 00:00:00.000000000 Z
11
+ date: 2025-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -107,6 +107,7 @@ files:
107
107
  - lib/togglr-client/models/feature_error_report.rb
108
108
  - lib/togglr-client/models/feature_health.rb
109
109
  - lib/togglr-client/models/health_response.rb
110
+ - lib/togglr-client/models/track_request.rb
110
111
  - lib/togglr-client/version.rb
111
112
  - lib/togglr.rb
112
113
  - lib/togglr/cache.rb
@@ -118,6 +119,7 @@ files:
118
119
  - lib/togglr/models.rb
119
120
  - lib/togglr/options.rb
120
121
  - lib/togglr/request_context.rb
122
+ - lib/togglr/track_event.rb
121
123
  - lib/togglr/version.rb
122
124
  - spec/examples.txt
123
125
  - spec/spec_helper.rb