posthog-ruby 3.17.0 → 3.19.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: 187ed0ab3a49d0137a8a5516792d00eb6baf5242ed6665df879563656a6541ac
4
- data.tar.gz: cf463f7135031a7d6975bac3eb72ad48ead37729c577738f80fe03e83a6c8f9c
3
+ metadata.gz: b23e0da0723e7582825d0888d8c6ffd9b3ad0a77e5eec513a40ab3de1faaa306
4
+ data.tar.gz: 1fc203c912147dc865cb342213ef8ccd3604e4d84f0b0005932c559aa30c8017
5
5
  SHA512:
6
- metadata.gz: 5f0906fded5049de9de8638ffee0345e7db4bee9a73387fcc602247a9e62193e011fa89358a1001d8818ec216116aec3750548e0c056a6488a7c3360784da038
7
- data.tar.gz: 5d7c036bfccdb96b1343287a7349c5ab95ec7184b88db2189f9ba4d805756e050edc3f14533517b79a30377253649ceb1ccecf95a0f9950fdc28812cf18ec629
6
+ metadata.gz: 8409ba6d5fb0801643cbd6556a1dabf41441ff522b85d612601ac8547482c5eb139ac68f21250f2f240654f08c03b3ca6cdc0409f57f1c6af9cdd880c677805f
7
+ data.tar.gz: c7a146c293985480777e6f5139588ca0d897170b8fe15412e9d60561711cb57bec53e70225b8b209916a50835f249b9816bece364be754a56e6f1118454feb52
@@ -53,7 +53,11 @@ module PostHog
53
53
 
54
54
  # @param opts [Hash] Client configuration.
55
55
  # @option opts [String, nil] :api_key Your project's API key. Missing or blank values disable the client.
56
- # @option opts [String, nil] :personal_api_key Your personal API key. Required for local feature flag evaluation.
56
+ # @option opts [String, nil] :secret_key The credential used for local feature flag evaluation and remote
57
+ # config. Accepts either a Personal API Key (`phx_...`) or a Project Secret API Key (`phs_...`). Required
58
+ # for local feature flag evaluation.
59
+ # @option opts [String, nil] :personal_api_key
60
+ # @deprecated Use +:secret_key+ instead. Retained as an alias; when both are supplied, +:secret_key+ wins.
57
61
  # @option opts [String] :host Fully qualified hostname of the PostHog server. Defaults to `https://us.i.posthog.com`.
58
62
  # @option opts [Integer] :max_queue_size Maximum number of calls to remain queued. Defaults to 10_000.
59
63
  # @option opts [Integer] :batch_size Maximum number of events to send in one async batch.
@@ -88,9 +92,18 @@ module PostHog
88
92
  symbolize_keys!(opts)
89
93
 
90
94
  opts[:api_key] = normalize_string_option(opts[:api_key])
95
+ opts[:secret_key] = normalize_string_option(opts[:secret_key], blank_as_nil: true)
91
96
  opts[:personal_api_key] = normalize_string_option(opts[:personal_api_key], blank_as_nil: true)
92
97
  opts[:host] = normalize_host_option(opts[:host])
93
98
 
99
+ if opts[:secret_key].nil? && !opts[:personal_api_key].nil?
100
+ logger.warn(
101
+ 'The :personal_api_key option is deprecated; use :secret_key instead. It accepts either a ' \
102
+ 'Personal API Key (phx_...) or a Project Secret API Key (phs_...).'
103
+ )
104
+ end
105
+ secret_key = opts[:secret_key] || opts[:personal_api_key]
106
+
94
107
  @queue = Queue.new
95
108
  @queue_mutex = Mutex.new
96
109
  @api_key = opts[:api_key]
@@ -119,7 +132,8 @@ module PostHog
119
132
  end
120
133
  @worker_thread = nil
121
134
  @feature_flags_poller = nil
122
- @personal_api_key = opts[:personal_api_key]
135
+ @secret_key = secret_key
136
+ @personal_api_key = secret_key
123
137
 
124
138
  if @disabled && !opts[:silence_disabled_client_error]
125
139
  logger.error('api_key is missing or empty after trimming whitespace; check your project API key')
@@ -142,7 +156,7 @@ module PostHog
142
156
  @feature_flags_poller =
143
157
  FeatureFlagsPoller.new(
144
158
  opts[:feature_flags_polling_interval],
145
- opts[:personal_api_key],
159
+ secret_key,
146
160
  @api_key,
147
161
  opts[:host],
148
162
  opts[:feature_flag_request_timeout_seconds] || Defaults::FeatureFlags::FLAG_REQUEST_TIMEOUT_SECONDS,
@@ -595,7 +609,8 @@ module PostHog
595
609
  id: definition[:id],
596
610
  version: nil,
597
611
  reason: FeatureFlagEvaluations::EVALUATED_LOCALLY_REASON,
598
- locally_evaluated: true
612
+ locally_evaluated: true,
613
+ has_experiment: definition[:has_experiment]
599
614
  )
600
615
  locally_evaluated_keys << key.to_s
601
616
  end
@@ -635,7 +650,8 @@ module PostHog
635
650
  id: metadata ? metadata.id : nil,
636
651
  version: metadata ? metadata.version : nil,
637
652
  reason: reason ? (reason.description || reason.code) : nil,
638
- locally_evaluated: false
653
+ locally_evaluated: false,
654
+ has_experiment: metadata&.has_experiment
639
655
  )
640
656
  end
641
657
  rescue StandardError => e
@@ -752,6 +768,7 @@ module PostHog
752
768
  # Remove internal information
753
769
  response.delete(:requestId)
754
770
  response.delete(:evaluatedAt)
771
+ response.delete(:flagDetails)
755
772
  response
756
773
  end
757
774
 
@@ -761,9 +778,9 @@ module PostHog
761
778
  def reload_feature_flags
762
779
  return if @disabled
763
780
 
764
- unless @personal_api_key
781
+ unless @secret_key
765
782
  logger.error(
766
- 'You need to specify a personal_api_key to locally evaluate feature flags'
783
+ 'You need to specify a secret_key to locally evaluate feature flags'
767
784
  )
768
785
  return
769
786
  end
@@ -907,7 +924,8 @@ module PostHog
907
924
  person_properties, group_properties = add_local_person_and_group_properties(
908
925
  groups, person_properties, group_properties
909
926
  )
910
- feature_flag_response, flag_was_locally_evaluated, request_id, evaluated_at, feature_flag_error, payload =
927
+ feature_flag_response, flag_was_locally_evaluated, request_id, evaluated_at, feature_flag_error, payload,
928
+ has_experiment =
911
929
  @feature_flags_poller.get_feature_flag(
912
930
  key, distinct_id, groups, person_properties, group_properties, only_evaluate_locally
913
931
  )
@@ -917,6 +935,7 @@ module PostHog
917
935
  '$feature_flag_response' => feature_flag_response,
918
936
  'locally_evaluated' => flag_was_locally_evaluated
919
937
  }
938
+ properties['$feature_flag_has_experiment'] = has_experiment unless has_experiment.nil?
920
939
  properties['$feature_flag_request_id'] = request_id if request_id
921
940
  properties['$feature_flag_evaluated_at'] = evaluated_at if evaluated_at
922
941
  properties['$feature_flag_error'] = feature_flag_error if feature_flag_error
@@ -68,7 +68,7 @@ module PostHog
68
68
  #
69
69
  # @api private
70
70
  class FeatureFlagMetadata
71
- attr_reader :id, :version, :payload, :description
71
+ attr_reader :id, :version, :payload, :description, :has_experiment
72
72
 
73
73
  # @param json [Hash] Raw metadata returned by /flags.
74
74
  def initialize(json)
@@ -77,6 +77,9 @@ module PostHog
77
77
  @version = json['version']
78
78
  @payload = json['payload']
79
79
  @description = json['description']
80
+ # Whether the flag is linked to an experiment. nil when the server
81
+ # (an older deployment) does not report the field.
82
+ @has_experiment = json['has_experiment']
80
83
  end
81
84
  end
82
85
  end
@@ -13,7 +13,7 @@ module PostHog
13
13
  EVALUATED_LOCALLY_REASON = 'Evaluated locally'
14
14
 
15
15
  EvaluatedFlagRecord = Struct.new(
16
- :key, :enabled, :variant, :payload, :id, :version, :reason, :locally_evaluated,
16
+ :key, :enabled, :variant, :payload, :id, :version, :reason, :locally_evaluated, :has_experiment,
17
17
  keyword_init: true
18
18
  )
19
19
 
@@ -169,6 +169,7 @@ module PostHog
169
169
  }
170
170
 
171
171
  if flag
172
+ properties['$feature_flag_has_experiment'] = flag.has_experiment unless flag.has_experiment.nil?
172
173
  properties['$feature_flag_payload'] = flag.payload unless flag.payload.nil?
173
174
  properties['$feature_flag_id'] = flag.id if flag.id
174
175
  properties['$feature_flag_version'] = flag.version if flag.version
@@ -30,7 +30,8 @@ module PostHog
30
30
  include PostHog::Utils
31
31
 
32
32
  # @param polling_interval [Integer, nil] Seconds between local feature flag definition polls.
33
- # @param personal_api_key [String, nil] Personal API key used to fetch local evaluation definitions.
33
+ # @param secret_key [String, nil] Credential used to fetch local evaluation definitions. Accepts either a
34
+ # Personal API Key (`phx_...`) or a Project Secret API Key (`phs_...`).
34
35
  # @param project_api_key [String] Project API key.
35
36
  # @param host [String] PostHog API host URL.
36
37
  # @param feature_flag_request_timeout_seconds [Integer] Timeout for feature flag requests.
@@ -41,7 +42,7 @@ module PostHog
41
42
  # Set to 0 to disable retrying.
42
43
  def initialize(
43
44
  polling_interval,
44
- personal_api_key,
45
+ secret_key,
45
46
  project_api_key,
46
47
  host,
47
48
  feature_flag_request_timeout_seconds,
@@ -50,7 +51,7 @@ module PostHog
50
51
  feature_flag_request_max_retries: nil
51
52
  )
52
53
  @polling_interval = polling_interval || 30
53
- @personal_api_key = personal_api_key
54
+ @secret_key = secret_key
54
55
  @project_api_key = project_api_key
55
56
  @host = host
56
57
  @feature_flags = Concurrent::Array.new
@@ -74,9 +75,9 @@ module PostHog
74
75
  execution_interval: polling_interval
75
76
  ) { _load_feature_flags }
76
77
 
77
- # If no personal API key, disable local evaluation & thus polling for definitions
78
- if @personal_api_key.nil?
79
- logger.info 'No personal API key provided, disabling local evaluation'
78
+ # If no secret_key, disable local evaluation & thus polling for definitions
79
+ if @secret_key.nil?
80
+ logger.info 'No secret_key provided, disabling local evaluation'
80
81
  @loaded_flags_successfully_once.make_true
81
82
  else
82
83
  # load once before timer
@@ -220,6 +221,11 @@ module PostHog
220
221
  end
221
222
 
222
223
  flag_was_locally_evaluated = !response.nil?
224
+ # Whether the flag is linked to an experiment, as reported by the server.
225
+ # Locally-evaluated flags carry it in the stored definition; remotely
226
+ # evaluated flags carry it in the response metadata. nil when the server
227
+ # (an older deployment) does not report it.
228
+ has_experiment = feature_flag[:has_experiment] if flag_was_locally_evaluated
223
229
 
224
230
  request_id = nil
225
231
  evaluated_at = nil
@@ -252,6 +258,9 @@ module PostHog
252
258
  payload = payloads[key]
253
259
  feature_flag_error = errors.join(',') unless errors.empty?
254
260
 
261
+ flag_detail = flags_data[:flagDetails]&.[](key.to_sym)
262
+ has_experiment = flag_detail&.metadata&.has_experiment
263
+
255
264
  logger.debug "Successfully computed flag remotely: #{key} -> #{response}"
256
265
  rescue Timeout::Error => e
257
266
  @on_error.call(-1, "Timeout while fetching flags remotely: #{e}")
@@ -265,7 +274,7 @@ module PostHog
265
274
  end
266
275
  end
267
276
 
268
- [response, flag_was_locally_evaluated, request_id, evaluated_at, feature_flag_error, payload]
277
+ [response, flag_was_locally_evaluated, request_id, evaluated_at, feature_flag_error, payload, has_experiment]
269
278
  end
270
279
 
271
280
  def get_all_flags(
@@ -323,10 +332,12 @@ module PostHog
323
332
  errors_while_computing = false
324
333
  quota_limited = nil
325
334
  status_code = nil
335
+ flag_details = nil
326
336
 
327
337
  if fallback_to_server && !only_evaluate_locally
328
338
  begin
329
339
  flags_and_payloads = get_flags(distinct_id, groups, person_properties, group_properties)
340
+ flag_details = flags_and_payloads[:flags]
330
341
  errors_while_computing = flags_and_payloads[:errorsWhileComputingFlags] || false
331
342
  quota_limited = flags_and_payloads[:quotaLimited]
332
343
  status_code = flags_and_payloads[:status]
@@ -367,6 +378,7 @@ module PostHog
367
378
  {
368
379
  featureFlags: flags,
369
380
  featureFlagPayloads: payloads,
381
+ flagDetails: flag_details,
370
382
  requestId: request_id,
371
383
  evaluatedAt: evaluated_at,
372
384
  errorsWhileComputingFlags: errors_while_computing,
@@ -1227,7 +1239,7 @@ module PostHog
1227
1239
  uri = URI("#{@host}/flags/definitions")
1228
1240
  uri.query = URI.encode_www_form([['token', @project_api_key], %w[send_cohorts true]])
1229
1241
  req = Net::HTTP::Get.new(uri)
1230
- req['Authorization'] = "Bearer #{@personal_api_key}"
1242
+ req['Authorization'] = "Bearer #{@secret_key}"
1231
1243
  req['If-None-Match'] = etag if etag
1232
1244
 
1233
1245
  _request(uri, req, nil, include_etag: true)
@@ -1253,7 +1265,7 @@ module PostHog
1253
1265
  uri.query = URI.encode_www_form([['token', @project_api_key]])
1254
1266
  req = Net::HTTP::Get.new(uri)
1255
1267
  req['Content-Type'] = 'application/json'
1256
- req['Authorization'] = "Bearer #{@personal_api_key}"
1268
+ req['Authorization'] = "Bearer #{@secret_key}"
1257
1269
 
1258
1270
  _request(uri, req, @feature_flag_request_timeout_seconds)
1259
1271
  end
@@ -52,7 +52,7 @@ module PostHog
52
52
  # cache = RedisFlagCache.new(redis, service_key: 'my-service')
53
53
  # client = PostHog::Client.new(
54
54
  # api_key: '<project_api_key>',
55
- # personal_api_key: '<personal_api_key>',
55
+ # secret_key: '<secret_key>',
56
56
  # flag_definition_cache_provider: cache
57
57
  # )
58
58
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PostHog
4
- VERSION = '3.17.0'
4
+ VERSION = '3.19.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: posthog-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.17.0
4
+ version: 3.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''