posthog-ruby 1.1.0 → 1.2.3
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 +4 -4
- data/bin/posthog +57 -39
- data/lib/posthog/backoff_policy.rb +3 -6
- data/lib/posthog/client.rb +58 -6
- data/lib/posthog/defaults.rb +8 -6
- data/lib/posthog/feature_flags.rb +129 -0
- data/lib/posthog/field_parser.rb +37 -29
- data/lib/posthog/logging.rb +8 -8
- data/lib/posthog/message_batch.rb +2 -1
- data/lib/posthog/response.rb +1 -2
- data/lib/posthog/transport.rb +16 -17
- data/lib/posthog/utils.rb +11 -12
- data/lib/posthog/version.rb +1 -1
- data/lib/posthog/worker.rb +1 -2
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b90a7b41e6a5a6fbbf48f555b9948e05b1d4caec377ca715a07701e5d655123
|
4
|
+
data.tar.gz: cab835193f7c6ecba36d5121201ef5c216b9bdf31762124811a1fa43754c29e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdee6bf1dc34a46de738bfa5c73386d6054677080a25ab79ea37311c01992963b4731a432e2705d96e6fc10f66fed21f8a0438d4b8be3117c866fb4dd622f3b4
|
7
|
+
data.tar.gz: 91f6a9b65ebbcd9d95536f8542b96b777d2a47ece861912331d5764973101c77818f6104bc1b857f4f458883fe159ed46bd8029eb6076de541705b15f11c70a7
|
data/bin/posthog
CHANGED
@@ -11,32 +11,39 @@ program :version, '1.0.0'
|
|
11
11
|
program :description, 'PostHog API'
|
12
12
|
|
13
13
|
def json_hash(str)
|
14
|
-
if str
|
15
|
-
return JSON.parse(str)
|
16
|
-
end
|
14
|
+
return JSON.parse(str) if str
|
17
15
|
end
|
18
16
|
|
19
17
|
command :capture do |c|
|
20
18
|
c.description = 'capture an event'
|
21
19
|
|
22
20
|
c.option '--api-key=<string>', String, 'The PostHog API Key'
|
23
|
-
c.option '--api-host=<url>',
|
24
|
-
|
21
|
+
c.option '--api-host=<url>',
|
22
|
+
String,
|
23
|
+
'The PostHog API URL host part (scheme+domain)'
|
24
|
+
c.option '--distinct-id=<distinct_id>',
|
25
|
+
String,
|
26
|
+
'The distinct id to send the event as'
|
25
27
|
c.option '--event=<event>', String, 'The event name to send with the event'
|
26
28
|
c.option '--properties=<properties>', 'The properties to send (JSON-encoded)'
|
27
29
|
|
28
30
|
c.action do |args, options|
|
29
|
-
posthog =
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
31
|
+
posthog =
|
32
|
+
PostHog::Client.new(
|
33
|
+
{
|
34
|
+
api_key: options.api_key,
|
35
|
+
api_host: options.api_host,
|
36
|
+
on_error: Proc.new { |status, msg| print msg }
|
37
|
+
}
|
38
|
+
)
|
39
|
+
|
40
|
+
posthog.capture(
|
41
|
+
{
|
42
|
+
distinct_id: options.distinct_id,
|
43
|
+
event: options.event,
|
44
|
+
properties: json_hash(options.properties)
|
45
|
+
}
|
46
|
+
)
|
40
47
|
|
41
48
|
posthog.flush
|
42
49
|
end
|
@@ -46,21 +53,30 @@ command :identify do |c|
|
|
46
53
|
c.description = 'identify the user'
|
47
54
|
|
48
55
|
c.option '--api-key=<api_key>', String, 'The PostHog API Key'
|
49
|
-
c.option '--api-host=<url>',
|
50
|
-
|
56
|
+
c.option '--api-host=<url>',
|
57
|
+
String,
|
58
|
+
'The PostHog API URL host part (scheme+domain)'
|
59
|
+
c.option '--distinct-id=<distinct_id>',
|
60
|
+
String,
|
61
|
+
'The distinct id to send the event as'
|
51
62
|
c.option '--properties=<properties>', 'The properties to send (JSON-encoded)'
|
52
63
|
|
53
64
|
c.action do |args, options|
|
54
|
-
posthog =
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
65
|
+
posthog =
|
66
|
+
PostHog::Client.new(
|
67
|
+
{
|
68
|
+
api_key: options.api_key,
|
69
|
+
api_host: options.api_host,
|
70
|
+
on_error: Proc.new { |status, msg| print msg }
|
71
|
+
}
|
72
|
+
)
|
73
|
+
|
74
|
+
posthog.identify(
|
75
|
+
{
|
76
|
+
distinct_id: options.distinct_id,
|
77
|
+
properties: json_hash(options.properties)
|
78
|
+
}
|
79
|
+
)
|
64
80
|
|
65
81
|
posthog.flush
|
66
82
|
end
|
@@ -70,21 +86,23 @@ command :alias do |c|
|
|
70
86
|
c.description = 'set an alias for a distinct id'
|
71
87
|
|
72
88
|
c.option '--api-key=<api_key>', String, 'The PostHog API Key'
|
73
|
-
c.option '--api-host=<url>',
|
89
|
+
c.option '--api-host=<url>',
|
90
|
+
String,
|
91
|
+
'The PostHog API URL host part (scheme+domain)'
|
74
92
|
c.option '--distinct-id=<distinct_id>', String, 'The distinct id'
|
75
93
|
c.option '--alias=<alias>', 'The alias to give to the distinct id'
|
76
94
|
|
77
95
|
c.action do |args, options|
|
78
|
-
posthog =
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
})
|
96
|
+
posthog =
|
97
|
+
PostHog::Client.new(
|
98
|
+
{
|
99
|
+
api_key: options.api_key,
|
100
|
+
api_host: options.api_host,
|
101
|
+
on_error: Proc.new { |status, msg| print msg }
|
102
|
+
}
|
103
|
+
)
|
104
|
+
|
105
|
+
posthog.alias({ distinct_id: options.distinct_id, alias: options.alias })
|
88
106
|
|
89
107
|
posthog.flush
|
90
108
|
end
|
@@ -15,7 +15,8 @@ class PostHog
|
|
15
15
|
@min_timeout_ms = opts[:min_timeout_ms] || MIN_TIMEOUT_MS
|
16
16
|
@max_timeout_ms = opts[:max_timeout_ms] || MAX_TIMEOUT_MS
|
17
17
|
@multiplier = opts[:multiplier] || MULTIPLIER
|
18
|
-
@randomization_factor =
|
18
|
+
@randomization_factor =
|
19
|
+
opts[:randomization_factor] || RANDOMIZATION_FACTOR
|
19
20
|
|
20
21
|
@attempts = 0
|
21
22
|
end
|
@@ -37,11 +38,7 @@ class PostHog
|
|
37
38
|
max_deviation = base * randomization_factor
|
38
39
|
deviation = random_number * max_deviation
|
39
40
|
|
40
|
-
|
41
|
-
base - deviation
|
42
|
-
else
|
43
|
-
base + deviation
|
44
|
-
end
|
41
|
+
random_number < 0.5 ? base - deviation : base + deviation
|
45
42
|
end
|
46
43
|
end
|
47
44
|
end
|
data/lib/posthog/client.rb
CHANGED
@@ -5,6 +5,7 @@ require 'posthog/defaults'
|
|
5
5
|
require 'posthog/logging'
|
6
6
|
require 'posthog/utils'
|
7
7
|
require 'posthog/worker'
|
8
|
+
require 'posthog/feature_flags'
|
8
9
|
|
9
10
|
class PostHog
|
10
11
|
class Client
|
@@ -25,9 +26,22 @@ class PostHog
|
|
25
26
|
@worker_mutex = Mutex.new
|
26
27
|
@worker = Worker.new(@queue, @api_key, opts)
|
27
28
|
@worker_thread = nil
|
29
|
+
@feature_flags_poller = nil
|
30
|
+
@personal_api_key = nil
|
28
31
|
|
29
32
|
check_api_key!
|
30
33
|
|
34
|
+
if opts[:personal_api_key]
|
35
|
+
@personal_api_key = opts[:personal_api_key]
|
36
|
+
@feature_flags_poller =
|
37
|
+
FeatureFlagsPoller.new(
|
38
|
+
opts[:feature_flags_polling_interval],
|
39
|
+
opts[:personal_api_key],
|
40
|
+
@api_key,
|
41
|
+
opts[:host]
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
31
45
|
at_exit { @worker_thread && @worker_thread[:should_exit] = true }
|
32
46
|
end
|
33
47
|
|
@@ -87,6 +101,47 @@ class PostHog
|
|
87
101
|
@queue.length
|
88
102
|
end
|
89
103
|
|
104
|
+
def is_feature_enabled(flag_key, distinct_id, default_value = false)
|
105
|
+
unless @personal_api_key
|
106
|
+
logger.error(
|
107
|
+
'You need to specify a personal_api_key to use feature flags'
|
108
|
+
)
|
109
|
+
return
|
110
|
+
end
|
111
|
+
is_enabled =
|
112
|
+
@feature_flags_poller.is_feature_enabled(
|
113
|
+
flag_key,
|
114
|
+
distinct_id,
|
115
|
+
default_value
|
116
|
+
)
|
117
|
+
capture(
|
118
|
+
{
|
119
|
+
'distinct_id': distinct_id,
|
120
|
+
'event': '$feature_flag_called',
|
121
|
+
'properties': {
|
122
|
+
'$feature_flag': flag_key,
|
123
|
+
'$feature_flag_response': is_enabled
|
124
|
+
}
|
125
|
+
}
|
126
|
+
)
|
127
|
+
return is_enabled
|
128
|
+
end
|
129
|
+
|
130
|
+
def reload_feature_flags
|
131
|
+
unless @personal_api_key
|
132
|
+
logger.error(
|
133
|
+
'You need to specify a personal_api_key to use feature flags'
|
134
|
+
)
|
135
|
+
return
|
136
|
+
end
|
137
|
+
@feature_flags_poller.load_feature_flags(true)
|
138
|
+
end
|
139
|
+
|
140
|
+
def shutdown
|
141
|
+
@feature_flags_poller.shutdown_poller
|
142
|
+
flush
|
143
|
+
end
|
144
|
+
|
90
145
|
private
|
91
146
|
|
92
147
|
# private: Enqueues the action.
|
@@ -104,8 +159,8 @@ class PostHog
|
|
104
159
|
else
|
105
160
|
logger.warn(
|
106
161
|
'Queue is full, dropping events. The :max_queue_size ' \
|
107
|
-
|
108
|
-
|
162
|
+
'configuration parameter can be increased to prevent this from ' \
|
163
|
+
'happening.'
|
109
164
|
)
|
110
165
|
false
|
111
166
|
end
|
@@ -120,9 +175,7 @@ class PostHog
|
|
120
175
|
return if worker_running?
|
121
176
|
@worker_mutex.synchronize do
|
122
177
|
return if worker_running?
|
123
|
-
@worker_thread = Thread.new
|
124
|
-
@worker.run
|
125
|
-
end
|
178
|
+
@worker_thread = Thread.new { @worker.run }
|
126
179
|
end
|
127
180
|
end
|
128
181
|
|
@@ -131,4 +184,3 @@ class PostHog
|
|
131
184
|
end
|
132
185
|
end
|
133
186
|
end
|
134
|
-
|
data/lib/posthog/defaults.rb
CHANGED
@@ -5,18 +5,20 @@ class PostHog
|
|
5
5
|
PORT = 443
|
6
6
|
PATH = '/batch/'
|
7
7
|
SSL = true
|
8
|
-
HEADERS = {
|
9
|
-
|
10
|
-
|
8
|
+
HEADERS = {
|
9
|
+
'Accept' => 'application/json',
|
10
|
+
'Content-Type' => 'application/json',
|
11
|
+
'User-Agent' => "posthog-ruby/#{PostHog::VERSION}"
|
12
|
+
}
|
11
13
|
RETRIES = 10
|
12
14
|
end
|
13
15
|
|
14
16
|
module Queue
|
15
|
-
MAX_SIZE =
|
17
|
+
MAX_SIZE = 10_000
|
16
18
|
end
|
17
19
|
|
18
20
|
module Message
|
19
|
-
MAX_BYTES =
|
21
|
+
MAX_BYTES = 32_768 # 32Kb
|
20
22
|
end
|
21
23
|
|
22
24
|
module MessageBatch
|
@@ -26,7 +28,7 @@ class PostHog
|
|
26
28
|
|
27
29
|
module BackoffPolicy
|
28
30
|
MIN_TIMEOUT_MS = 100
|
29
|
-
MAX_TIMEOUT_MS =
|
31
|
+
MAX_TIMEOUT_MS = 10_000
|
30
32
|
MULTIPLIER = 1.5
|
31
33
|
RANDOMIZATION_FACTOR = 0.5
|
32
34
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
require 'net/http'
|
3
|
+
require 'json'
|
4
|
+
require 'posthog/version'
|
5
|
+
require 'posthog/logging'
|
6
|
+
require 'digest'
|
7
|
+
class PostHog
|
8
|
+
class FeatureFlagsPoller
|
9
|
+
include PostHog::Logging
|
10
|
+
|
11
|
+
def initialize(polling_interval, personal_api_key, project_api_key, host)
|
12
|
+
@polling_interval = polling_interval || 60 * 5
|
13
|
+
@personal_api_key = personal_api_key
|
14
|
+
@project_api_key = project_api_key
|
15
|
+
@host = host || 'app.posthog.com'
|
16
|
+
@feature_flags = Concurrent::Array.new
|
17
|
+
@loaded_flags_successfully_once = Concurrent::AtomicBoolean.new
|
18
|
+
|
19
|
+
@task =
|
20
|
+
Concurrent::TimerTask.new(
|
21
|
+
execution_interval: polling_interval,
|
22
|
+
timeout_interval: 15
|
23
|
+
) { _load_feature_flags }
|
24
|
+
|
25
|
+
# load once before timer
|
26
|
+
load_feature_flags
|
27
|
+
@task.execute
|
28
|
+
end
|
29
|
+
|
30
|
+
def is_feature_enabled(key, distinct_id, default_result = false)
|
31
|
+
# make sure they're loaded on first run
|
32
|
+
load_feature_flags
|
33
|
+
|
34
|
+
return default_result unless @loaded_flags_successfully_once
|
35
|
+
|
36
|
+
feature_flag = nil
|
37
|
+
|
38
|
+
|
39
|
+
@feature_flags.each do |flag|
|
40
|
+
if key == flag['key']
|
41
|
+
feature_flag = flag
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
return default_result if !feature_flag
|
47
|
+
|
48
|
+
flag_rollout_pctg =
|
49
|
+
if feature_flag['rollout_percentage']
|
50
|
+
feature_flag['rollout_percentage']
|
51
|
+
else
|
52
|
+
100
|
53
|
+
end
|
54
|
+
if feature_flag['is_simple_flag']
|
55
|
+
return is_simple_flag_enabled(key, distinct_id, flag_rollout_pctg)
|
56
|
+
else
|
57
|
+
data = { 'distinct_id' => distinct_id }
|
58
|
+
res = _request('POST', 'decide', false, data)
|
59
|
+
return res['featureFlags'].include? key
|
60
|
+
end
|
61
|
+
|
62
|
+
return false
|
63
|
+
end
|
64
|
+
|
65
|
+
def is_simple_flag_enabled(key, distinct_id, rollout_percentage)
|
66
|
+
hash = Digest::SHA1.hexdigest "#{key}.#{distinct_id}"
|
67
|
+
return(
|
68
|
+
(Integer(hash[0..14], 16).to_f / 0xfffffffffffffff) <=
|
69
|
+
(rollout_percentage / 100)
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
def load_feature_flags(force_reload = false)
|
74
|
+
if @loaded_flags_successfully_once.false? || force_reload
|
75
|
+
_load_feature_flags
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def shutdown_poller()
|
80
|
+
@task.shutdown
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def _load_feature_flags()
|
86
|
+
res = _request('GET', 'api/feature_flag', true)
|
87
|
+
@feature_flags.clear
|
88
|
+
@feature_flags = res['results'].filter { |flag| flag['active'] }
|
89
|
+
if @loaded_flags_successfully_once.false?
|
90
|
+
@loaded_flags_successfully_once.make_true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def _request(method, endpoint, use_personal_api_key = false, data = {})
|
95
|
+
uri = URI("https://#{@host}/#{endpoint}/?token=#{@project_api_key}")
|
96
|
+
req = nil
|
97
|
+
if use_personal_api_key
|
98
|
+
req = Net::HTTP::Get.new(uri)
|
99
|
+
req['Authorization'] = "Bearer #{@personal_api_key}"
|
100
|
+
else
|
101
|
+
req = Net::HTTP::Post.new(uri)
|
102
|
+
req['Content-Type'] = 'application/json'
|
103
|
+
data['token'] = @project_api_key
|
104
|
+
req.body = data.to_json
|
105
|
+
end
|
106
|
+
|
107
|
+
req['User-Agent'] = "posthog-ruby#{PostHog::VERSION}"
|
108
|
+
|
109
|
+
begin
|
110
|
+
res_body = nil
|
111
|
+
res =
|
112
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
113
|
+
res = http.request(req)
|
114
|
+
res_body = JSON.parse(res.body)
|
115
|
+
return res_body
|
116
|
+
end
|
117
|
+
rescue Timeout::Error,
|
118
|
+
Errno::EINVAL,
|
119
|
+
Errno::ECONNRESET,
|
120
|
+
EOFError,
|
121
|
+
Net::HTTPBadResponse,
|
122
|
+
Net::HTTPHeaderSyntaxError,
|
123
|
+
Net::ProtocolError => e
|
124
|
+
logger.debug("Unable to complete request to #{uri}")
|
125
|
+
throw e
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/posthog/field_parser.rb
CHANGED
@@ -18,11 +18,13 @@ class PostHog
|
|
18
18
|
|
19
19
|
isoify_dates! properties
|
20
20
|
|
21
|
-
common.merge(
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
common.merge(
|
22
|
+
{
|
23
|
+
type: 'capture',
|
24
|
+
event: event.to_s,
|
25
|
+
properties: properties.merge(common[:properties] || {})
|
26
|
+
}
|
27
|
+
)
|
26
28
|
end
|
27
29
|
|
28
30
|
# In addition to the common fields, identify accepts:
|
@@ -36,12 +38,14 @@ class PostHog
|
|
36
38
|
|
37
39
|
isoify_dates! properties
|
38
40
|
|
39
|
-
common.merge(
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
common.merge(
|
42
|
+
{
|
43
|
+
type: 'identify',
|
44
|
+
event: '$identify',
|
45
|
+
'$set': properties,
|
46
|
+
properties: properties.merge(common[:properties] || {})
|
47
|
+
}
|
48
|
+
)
|
45
49
|
end
|
46
50
|
|
47
51
|
# In addition to the common fields, alias accepts:
|
@@ -55,15 +59,17 @@ class PostHog
|
|
55
59
|
alias_field = fields[:alias]
|
56
60
|
check_presence! alias_field, 'alias'
|
57
61
|
|
58
|
-
common.merge(
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
:
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
common.merge(
|
63
|
+
{
|
64
|
+
type: 'alias',
|
65
|
+
event: '$create_alias',
|
66
|
+
distinct_id: nil,
|
67
|
+
properties:
|
68
|
+
{ distinct_id: distinct_id, alias: alias_field }.merge(
|
69
|
+
common[:properties] || {}
|
70
|
+
)
|
71
|
+
}
|
72
|
+
)
|
67
73
|
end
|
68
74
|
|
69
75
|
private
|
@@ -82,21 +88,23 @@ class PostHog
|
|
82
88
|
check_presence! distinct_id, 'distinct_id'
|
83
89
|
|
84
90
|
parsed = {
|
85
|
-
:
|
86
|
-
:
|
87
|
-
:
|
88
|
-
:
|
89
|
-
:
|
90
|
-
:
|
91
|
-
|
92
|
-
|
91
|
+
timestamp: datetime_in_iso8601(timestamp),
|
92
|
+
library: 'posthog-ruby',
|
93
|
+
library_version: PostHog::VERSION.to_s,
|
94
|
+
messageId: message_id,
|
95
|
+
distinct_id: distinct_id,
|
96
|
+
properties: {
|
97
|
+
'$lib' => 'posthog-ruby',
|
98
|
+
'$lib_version' => PostHog::VERSION.to_s
|
93
99
|
}
|
94
100
|
}
|
95
101
|
parsed
|
96
102
|
end
|
97
103
|
|
98
104
|
def check_timestamp!(timestamp)
|
99
|
-
|
105
|
+
unless timestamp.is_a? Time
|
106
|
+
raise ArgumentError, 'Timestamp must be a Time'
|
107
|
+
end
|
100
108
|
end
|
101
109
|
|
102
110
|
# private: Ensures that a string is non-empty
|
data/lib/posthog/logging.rb
CHANGED
@@ -30,13 +30,14 @@ class PostHog
|
|
30
30
|
def logger
|
31
31
|
return @logger if @logger
|
32
32
|
|
33
|
-
base_logger =
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
base_logger =
|
34
|
+
if defined?(Rails)
|
35
|
+
Rails.logger
|
36
|
+
else
|
37
|
+
logger = Logger.new STDOUT
|
38
|
+
logger.progname = 'PostHog'
|
39
|
+
logger
|
40
|
+
end
|
40
41
|
@logger = PrefixedLogger.new(base_logger, '[posthog-ruby]')
|
41
42
|
end
|
42
43
|
|
@@ -56,4 +57,3 @@ class PostHog
|
|
56
57
|
end
|
57
58
|
end
|
58
59
|
end
|
59
|
-
|
data/lib/posthog/response.rb
CHANGED
data/lib/posthog/transport.rb
CHANGED
@@ -26,13 +26,15 @@ class PostHog
|
|
26
26
|
@headers = options[:headers] || HEADERS
|
27
27
|
@path = options[:path] || PATH
|
28
28
|
@retries = options[:retries] || RETRIES
|
29
|
-
@backoff_policy =
|
30
|
-
options[:backoff_policy] || PostHog::BackoffPolicy.new
|
29
|
+
@backoff_policy = options[:backoff_policy] || PostHog::BackoffPolicy.new
|
31
30
|
|
32
31
|
http = Net::HTTP.new(options[:host], options[:port])
|
33
32
|
http.use_ssl = options[:ssl]
|
34
33
|
http.read_timeout = 8
|
35
34
|
http.open_timeout = 4
|
35
|
+
if options[:skip_ssl_verification]
|
36
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
37
|
+
end
|
36
38
|
|
37
39
|
@http = http
|
38
40
|
end
|
@@ -43,15 +45,16 @@ class PostHog
|
|
43
45
|
def send(api_key, batch)
|
44
46
|
logger.debug("Sending request for #{batch.length} items")
|
45
47
|
|
46
|
-
last_response, exception =
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
last_response, exception =
|
49
|
+
retry_with_backoff(@retries) do
|
50
|
+
status_code, body = send_request(api_key, batch)
|
51
|
+
error = JSON.parse(body)['error']
|
52
|
+
should_retry = should_retry_request?(status_code, body)
|
53
|
+
logger.debug("Response status code: #{status_code}")
|
54
|
+
logger.debug("Response error: #{error}") if error
|
52
55
|
|
53
|
-
|
54
|
-
|
56
|
+
[Response.new(status_code, error), should_retry]
|
57
|
+
end
|
55
58
|
|
56
59
|
if exception
|
57
60
|
logger.error(exception.message)
|
@@ -95,7 +98,7 @@ class PostHog
|
|
95
98
|
|
96
99
|
begin
|
97
100
|
result, should_retry = yield
|
98
|
-
return
|
101
|
+
return result, nil unless should_retry
|
99
102
|
rescue StandardError => e
|
100
103
|
should_retry = true
|
101
104
|
caught_exception = e
|
@@ -112,16 +115,13 @@ class PostHog
|
|
112
115
|
|
113
116
|
# Sends a request for the batch, returns [status_code, body]
|
114
117
|
def send_request(api_key, batch)
|
115
|
-
payload = JSON.generate(
|
116
|
-
api_key: api_key,
|
117
|
-
batch: batch
|
118
|
-
)
|
118
|
+
payload = JSON.generate(api_key: api_key, batch: batch)
|
119
119
|
|
120
120
|
request = Net::HTTP::Post.new(@path, @headers)
|
121
121
|
|
122
122
|
if self.class.stub
|
123
123
|
logger.debug "stubbed request to #{@path}: " \
|
124
|
-
|
124
|
+
"api key = #{api_key}, batch = #{JSON.generate(batch)}"
|
125
125
|
|
126
126
|
[200, '{}']
|
127
127
|
else
|
@@ -140,4 +140,3 @@ class PostHog
|
|
140
140
|
end
|
141
141
|
end
|
142
142
|
end
|
143
|
-
|
data/lib/posthog/utils.rb
CHANGED
@@ -7,9 +7,7 @@ class PostHog
|
|
7
7
|
# public: Return a new hash with keys converted from strings to symbols
|
8
8
|
#
|
9
9
|
def symbolize_keys(hash)
|
10
|
-
hash.each_with_object({})
|
11
|
-
memo[k.to_sym] = v
|
12
|
-
end
|
10
|
+
hash.each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v }
|
13
11
|
end
|
14
12
|
|
15
13
|
# public: Convert hash keys from strings to symbols in place
|
@@ -21,9 +19,7 @@ class PostHog
|
|
21
19
|
# public: Return a new hash with keys as strings
|
22
20
|
#
|
23
21
|
def stringify_keys(hash)
|
24
|
-
hash.each_with_object({})
|
25
|
-
memo[k.to_s] = v
|
26
|
-
end
|
22
|
+
hash.each_with_object({}) { |(k, v), memo| memo[k.to_s] = v }
|
27
23
|
end
|
28
24
|
|
29
25
|
# public: Returns a new hash with all the date values in the into iso8601
|
@@ -64,9 +60,8 @@ class PostHog
|
|
64
60
|
end
|
65
61
|
|
66
62
|
def time_in_iso8601(time, fraction_digits = 3)
|
67
|
-
fraction =
|
68
|
-
|
69
|
-
end
|
63
|
+
fraction =
|
64
|
+
(('.%06i' % time.usec)[0, fraction_digits + 1] if fraction_digits > 0)
|
70
65
|
|
71
66
|
"#{time.strftime('%Y-%m-%dT%H:%M:%S')}#{fraction}#{formatted_offset(time, true, 'Z')}"
|
72
67
|
end
|
@@ -76,15 +71,19 @@ class PostHog
|
|
76
71
|
end
|
77
72
|
|
78
73
|
def formatted_offset(time, colon = true, alternate_utc_string = nil)
|
79
|
-
time.utc? && alternate_utc_string ||
|
74
|
+
time.utc? && alternate_utc_string ||
|
75
|
+
seconds_to_utc_offset(time.utc_offset, colon)
|
80
76
|
end
|
81
77
|
|
82
78
|
def seconds_to_utc_offset(seconds, colon = true)
|
83
|
-
(colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON) % [
|
79
|
+
(colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON) % [
|
80
|
+
(seconds < 0 ? '-' : '+'),
|
81
|
+
(seconds.abs / 3600),
|
82
|
+
((seconds.abs % 3600) / 60)
|
83
|
+
]
|
84
84
|
end
|
85
85
|
|
86
86
|
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
|
87
87
|
UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '')
|
88
88
|
end
|
89
89
|
end
|
90
|
-
|
data/lib/posthog/version.rb
CHANGED
data/lib/posthog/worker.rb
CHANGED
@@ -28,7 +28,7 @@ class PostHog
|
|
28
28
|
batch_size = options[:batch_size] || Defaults::MessageBatch::MAX_SIZE
|
29
29
|
@batch = MessageBatch.new(batch_size)
|
30
30
|
@lock = Mutex.new
|
31
|
-
@transport = Transport.new api_host: options[:api_host]
|
31
|
+
@transport = Transport.new api_host: options[:api_host], skip_ssl_verification: options[:skip_ssl_verification]
|
32
32
|
end
|
33
33
|
|
34
34
|
# public: Continuously runs the loop to check for new events
|
@@ -65,4 +65,3 @@ class PostHog
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
68
|
-
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: posthog-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ''
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: concurrent-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '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'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: commander
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -135,6 +149,7 @@ files:
|
|
135
149
|
- lib/posthog/backoff_policy.rb
|
136
150
|
- lib/posthog/client.rb
|
137
151
|
- lib/posthog/defaults.rb
|
152
|
+
- lib/posthog/feature_flags.rb
|
138
153
|
- lib/posthog/field_parser.rb
|
139
154
|
- lib/posthog/logging.rb
|
140
155
|
- lib/posthog/message_batch.rb
|
@@ -162,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
177
|
- !ruby/object:Gem::Version
|
163
178
|
version: '0'
|
164
179
|
requirements: []
|
165
|
-
rubygems_version: 3.0.3
|
180
|
+
rubygems_version: 3.0.3.1
|
166
181
|
signing_key:
|
167
182
|
specification_version: 4
|
168
183
|
summary: PostHog library
|