userlist 0.7.1 → 0.8.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 +4 -4
- data/.github/workflows/test.yml +3 -5
- data/.rspec +2 -0
- data/.rubocop.yml +3 -0
- data/Gemfile +3 -1
- data/README.md +9 -9
- data/lib/userlist/config.rb +1 -1
- data/lib/userlist/push/client.rb +8 -3
- data/lib/userlist/push/resource.rb +8 -8
- data/lib/userlist/push/strategies/active_job/worker.rb +16 -0
- data/lib/userlist/push/strategies/active_job.rb +47 -0
- data/lib/userlist/push/strategies/direct.rb +9 -1
- data/lib/userlist/push/strategies/threaded/worker.rb +9 -1
- data/lib/userlist/push/strategies.rb +10 -2
- data/lib/userlist/retryable.rb +49 -0
- data/lib/userlist/version.rb +1 -1
- data/lib/userlist.rb +5 -0
- data/userlist.gemspec +4 -2
- metadata +12 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee98b5cd1e17b91ed12fce427d0c87ec4593da547349015ca4ed9272dba73100
|
4
|
+
data.tar.gz: 3ec9d567b7c8c1894841483cd136c76db4e1a667eac7101541fcd3d232b20a8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68340e08984d801834f1f853102119df452da6455bdca3b1eedf0d743d829c750fc81e234ef661f964402e44173b9951a891f15cb9ed0665929d525fbd0b03df
|
7
|
+
data.tar.gz: 1d6e7976d2e5486506306b2623135b4addf4cb7c6b6f3c3d81e30fde6d4fb733eb9d289027812ae6390d82b0b7cf4a5742104b0496d88372b0e720a8ed89402d
|
data/.github/workflows/test.yml
CHANGED
@@ -5,14 +5,12 @@ jobs:
|
|
5
5
|
strategy:
|
6
6
|
matrix:
|
7
7
|
ruby:
|
8
|
-
- 2.4
|
9
|
-
- 2.5
|
10
|
-
- 2.6
|
11
|
-
- 2.7
|
12
8
|
- 3.0
|
9
|
+
- 3.1
|
10
|
+
- 3.2
|
13
11
|
runs-on: ubuntu-latest
|
14
12
|
steps:
|
15
|
-
- uses: actions/checkout@
|
13
|
+
- uses: actions/checkout@v3
|
16
14
|
- uses: ruby/setup-ruby@v1
|
17
15
|
with:
|
18
16
|
ruby-version: ${{ matrix.ruby }}
|
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -44,15 +44,15 @@ end
|
|
44
44
|
|
45
45
|
The possible configuration values are listed in the table below.
|
46
46
|
|
47
|
-
| Name | Default value | Description
|
48
|
-
| ----------------------- | ---------------------------- |
|
49
|
-
| `push_key` | `nil` | The push key for your account. See [Push API settings](https://app.userlist.com/settings/push).
|
50
|
-
| `push_id` | `nil` | The push id for your account. See [Push API settings](https://app.userlist.com/settings/push).
|
51
|
-
| `push_endpoint` | `https://push.userlist.com/` | The HTTP endpoint that the library will send data to.
|
52
|
-
| `push_strategy` | `:threaded` | The strategy to use to send data to the HTTP endpoint. Possible values are `:threaded`, `:sidekiq`, `:direct`, and `:null`. |
|
53
|
-
| `push_strategy_options` | `{}` | Any additional options for the push strategy.
|
54
|
-
| `log_level` | `:warn` | The log level for Userlist related log messages. Possible values are `:debug`, `:error`, `:fatal`, `:info`, and `:warn`
|
55
|
-
| `token_lifetime` | `3600` | The lifetime of generated in-app messages tokens in seconds
|
47
|
+
| Name | Default value | Description |
|
48
|
+
| ----------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
49
|
+
| `push_key` | `nil` | The push key for your account. See [Push API settings](https://app.userlist.com/settings/push). |
|
50
|
+
| `push_id` | `nil` | The push id for your account. See [Push API settings](https://app.userlist.com/settings/push). |
|
51
|
+
| `push_endpoint` | `https://push.userlist.com/` | The HTTP endpoint that the library will send data to. |
|
52
|
+
| `push_strategy` | `:threaded` | The strategy to use to send data to the HTTP endpoint. Possible values are `:threaded`, `:sidekiq`, `:active_job`, `:direct`, and `:null`. |
|
53
|
+
| `push_strategy_options` | `{}` | Any additional options for the push strategy. |
|
54
|
+
| `log_level` | `:warn` | The log level for Userlist related log messages. Possible values are `:debug`, `:error`, `:fatal`, `:info`, and `:warn` |
|
55
|
+
| `token_lifetime` | `3600` | The lifetime of generated in-app messages tokens in seconds |
|
56
56
|
|
57
57
|
### Disabling in development and test environments
|
58
58
|
|
data/lib/userlist/config.rb
CHANGED
data/lib/userlist/push/client.rb
CHANGED
@@ -27,8 +27,8 @@ module Userlist
|
|
27
27
|
request(Net::HTTP::Put, endpoint, payload)
|
28
28
|
end
|
29
29
|
|
30
|
-
def delete(endpoint)
|
31
|
-
request(Net::HTTP::Delete, endpoint)
|
30
|
+
def delete(endpoint, payload = nil)
|
31
|
+
request(Net::HTTP::Delete, endpoint, payload)
|
32
32
|
end
|
33
33
|
|
34
34
|
private
|
@@ -57,7 +57,12 @@ module Userlist
|
|
57
57
|
|
58
58
|
logger.debug "Sending #{request.method} to #{URI.join(endpoint, request.path)} with body #{request.body}"
|
59
59
|
|
60
|
-
http.
|
60
|
+
http.start unless http.started?
|
61
|
+
response = http.request(request)
|
62
|
+
|
63
|
+
logger.debug "Recieved #{response.code} #{response.message} with body #{response.body}"
|
64
|
+
|
65
|
+
response
|
61
66
|
end
|
62
67
|
|
63
68
|
def endpoint
|
@@ -33,9 +33,9 @@ module Userlist
|
|
33
33
|
relationships[name.to_sym] = { type: type }
|
34
34
|
|
35
35
|
generated_methods.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
36
|
-
def #{name}
|
37
|
-
#{type}.from_payload(payload[:#{name}], config)
|
38
|
-
end
|
36
|
+
def #{name} # def company
|
37
|
+
#{type}.from_payload(payload[:#{name}], config) # Company.from_payload(payload[:company], config)
|
38
|
+
end # end
|
39
39
|
RUBY
|
40
40
|
end
|
41
41
|
|
@@ -43,11 +43,11 @@ module Userlist
|
|
43
43
|
relationships[name.to_sym] = options
|
44
44
|
|
45
45
|
generated_methods.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
46
|
-
def #{name}
|
47
|
-
relationship = self.class.relationships[:#{name}]
|
48
|
-
|
49
|
-
ResourceCollection.new(payload[:#{name}], relationship, self, config)
|
50
|
-
end
|
46
|
+
def #{name} # def companies
|
47
|
+
relationship = self.class.relationships[:#{name}] # relationship = self.class.relationships[:companies]
|
48
|
+
#
|
49
|
+
ResourceCollection.new(payload[:#{name}], relationship, self, config) # ResourceCollection.new(payload[:companies], relationship, self, config)
|
50
|
+
end #
|
51
51
|
RUBY
|
52
52
|
end
|
53
53
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'active_job'
|
2
|
+
|
3
|
+
module Userlist
|
4
|
+
class Push
|
5
|
+
module Strategies
|
6
|
+
class ActiveJob
|
7
|
+
class Worker < ::ActiveJob::Base
|
8
|
+
def perform(method, *args)
|
9
|
+
client = Userlist::Push::Client.new
|
10
|
+
client.public_send(method, *args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'active_job'
|
2
|
+
|
3
|
+
require 'userlist/push/strategies/active_job/worker'
|
4
|
+
|
5
|
+
module Userlist
|
6
|
+
class Push
|
7
|
+
module Strategies
|
8
|
+
class ActiveJob
|
9
|
+
def initialize(config = {})
|
10
|
+
@config = Userlist.config.merge(config)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(*args)
|
14
|
+
options = default_options.merge(self.options)
|
15
|
+
|
16
|
+
worker_name = options.delete(:class)
|
17
|
+
worker_class = Object.const_get(worker_name)
|
18
|
+
worker_class
|
19
|
+
.set(options)
|
20
|
+
.perform_later(*normalize(args))
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :config
|
26
|
+
|
27
|
+
def options
|
28
|
+
@options ||= begin
|
29
|
+
options = config.push_strategy_options || {}
|
30
|
+
options.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def default_options
|
35
|
+
{
|
36
|
+
class: 'Userlist::Push::Strategies::ActiveJob::Worker',
|
37
|
+
queue: 'default'
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def normalize(args)
|
42
|
+
JSON.parse(JSON.generate(args))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -7,7 +7,7 @@ module Userlist
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(*args)
|
10
|
-
client.public_send(*args)
|
10
|
+
retryable.attempt { client.public_send(*args) }
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
@@ -17,6 +17,14 @@ module Userlist
|
|
17
17
|
def client
|
18
18
|
@client ||= Userlist::Push::Client.new(config)
|
19
19
|
end
|
20
|
+
|
21
|
+
def retryable
|
22
|
+
@retryable ||= Userlist::Retryable.new do |response|
|
23
|
+
status = response.code.to_i
|
24
|
+
|
25
|
+
status >= 500 || status == 429
|
26
|
+
end
|
27
|
+
end
|
20
28
|
end
|
21
29
|
end
|
22
30
|
end
|
@@ -22,7 +22,7 @@ module Userlist
|
|
22
22
|
method, *args = *queue.pop
|
23
23
|
break if method == :stop
|
24
24
|
|
25
|
-
client.public_send(method, *args)
|
25
|
+
retryable.attempt { client.public_send(method, *args) }
|
26
26
|
rescue StandardError => e
|
27
27
|
logger.error "Failed to deliver payload: [#{e.class.name}] #{e.message}"
|
28
28
|
end
|
@@ -44,6 +44,14 @@ module Userlist
|
|
44
44
|
def client
|
45
45
|
@client ||= Userlist::Push::Client.new(config)
|
46
46
|
end
|
47
|
+
|
48
|
+
def retryable
|
49
|
+
@retryable ||= Userlist::Retryable.new do |response|
|
50
|
+
status = response.code.to_i
|
51
|
+
|
52
|
+
status >= 500 || status == 429
|
53
|
+
end
|
54
|
+
end
|
47
55
|
end
|
48
56
|
end
|
49
57
|
end
|
@@ -12,13 +12,16 @@ module Userlist
|
|
12
12
|
return strategy unless strategy.is_a?(Symbol) || strategy.is_a?(String)
|
13
13
|
|
14
14
|
require_strategy(strategy)
|
15
|
-
|
15
|
+
|
16
|
+
class_name = classify_strategy(strategy)
|
17
|
+
const_get(class_name, false)
|
16
18
|
end
|
17
19
|
|
18
20
|
def self.strategy_defined?(strategy)
|
19
21
|
return true unless strategy.is_a?(Symbol) || strategy.is_a?(String)
|
20
22
|
|
21
|
-
|
23
|
+
class_name = classify_strategy(strategy)
|
24
|
+
const_defined?(class_name, false)
|
22
25
|
end
|
23
26
|
|
24
27
|
def self.require_strategy(strategy)
|
@@ -26,6 +29,11 @@ module Userlist
|
|
26
29
|
|
27
30
|
require("userlist/push/strategies/#{strategy}") unless strategy_defined?(strategy)
|
28
31
|
end
|
32
|
+
|
33
|
+
def self.classify_strategy(strategy)
|
34
|
+
strategy.to_s.split('_').map(&:capitalize).join
|
35
|
+
end
|
36
|
+
private_class_method :classify_strategy
|
29
37
|
end
|
30
38
|
end
|
31
39
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Userlist
|
2
|
+
class Retryable
|
3
|
+
include Userlist::Logging
|
4
|
+
|
5
|
+
RETRIES = 10
|
6
|
+
DELAY = 100
|
7
|
+
MULTIPLIER = 2
|
8
|
+
MAX_DELAY = 10_000
|
9
|
+
|
10
|
+
def initialize(retries: RETRIES, delay: DELAY, max_delay: MAX_DELAY, multiplier: MULTIPLIER, &retry_check)
|
11
|
+
@retries = retries
|
12
|
+
@delay = delay
|
13
|
+
@max_delay = max_delay
|
14
|
+
@multiplier = multiplier
|
15
|
+
@retry_check = retry_check
|
16
|
+
end
|
17
|
+
|
18
|
+
def retry?(value)
|
19
|
+
@retry_check.call(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
def attempt
|
23
|
+
(0..@retries).each do |attempt|
|
24
|
+
if attempt.positive?
|
25
|
+
milliseconds = delay(attempt)
|
26
|
+
logger.debug "Retrying in #{milliseconds}ms, #{@retries - attempt} retries left"
|
27
|
+
sleep(milliseconds / 1000.0)
|
28
|
+
end
|
29
|
+
|
30
|
+
result = yield
|
31
|
+
|
32
|
+
return result unless retry?(result)
|
33
|
+
end
|
34
|
+
|
35
|
+
logger.debug 'Retries exhausted, giving up'
|
36
|
+
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def delay(attempt)
|
43
|
+
[
|
44
|
+
@delay * (@multiplier ** attempt),
|
45
|
+
@max_delay
|
46
|
+
].min
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/userlist/version.rb
CHANGED
data/lib/userlist.rb
CHANGED
@@ -3,6 +3,7 @@ require 'logger'
|
|
3
3
|
require 'userlist/version'
|
4
4
|
require 'userlist/config'
|
5
5
|
require 'userlist/logging'
|
6
|
+
require 'userlist/retryable'
|
6
7
|
require 'userlist/push'
|
7
8
|
require 'userlist/token'
|
8
9
|
|
@@ -51,6 +52,10 @@ module Userlist
|
|
51
52
|
yield config
|
52
53
|
end
|
53
54
|
|
55
|
+
def reset!
|
56
|
+
@config = nil
|
57
|
+
end
|
58
|
+
|
54
59
|
attr_writer :logger
|
55
60
|
end
|
56
61
|
end
|
data/userlist.gemspec
CHANGED
@@ -23,7 +23,9 @@ Gem::Specification.new do |spec|
|
|
23
23
|
|
24
24
|
spec.add_development_dependency 'bundler', '>= 1.15'
|
25
25
|
spec.add_development_dependency 'jwt', '~> 2.2'
|
26
|
-
spec.add_development_dependency 'rake', '~>
|
26
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
27
27
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
28
|
-
spec.add_development_dependency 'webmock', '~>
|
28
|
+
spec.add_development_dependency 'webmock', '~> 3.18'
|
29
|
+
|
30
|
+
spec.metadata = { 'rubygems_mfa_required' => 'true' }
|
29
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: userlist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benedikt Deicke
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,20 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
48
|
-
- - ">="
|
49
|
-
- !ruby/object:Gem::Version
|
50
|
-
version: 12.3.3
|
47
|
+
version: '13.0'
|
51
48
|
type: :development
|
52
49
|
prerelease: false
|
53
50
|
version_requirements: !ruby/object:Gem::Requirement
|
54
51
|
requirements:
|
55
52
|
- - "~>"
|
56
53
|
- !ruby/object:Gem::Version
|
57
|
-
version: '
|
58
|
-
- - ">="
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
version: 12.3.3
|
54
|
+
version: '13.0'
|
61
55
|
- !ruby/object:Gem::Dependency
|
62
56
|
name: rspec
|
63
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -78,14 +72,14 @@ dependencies:
|
|
78
72
|
requirements:
|
79
73
|
- - "~>"
|
80
74
|
- !ruby/object:Gem::Version
|
81
|
-
version: '
|
75
|
+
version: '3.18'
|
82
76
|
type: :development
|
83
77
|
prerelease: false
|
84
78
|
version_requirements: !ruby/object:Gem::Requirement
|
85
79
|
requirements:
|
86
80
|
- - "~>"
|
87
81
|
- !ruby/object:Gem::Version
|
88
|
-
version: '
|
82
|
+
version: '3.18'
|
89
83
|
description:
|
90
84
|
email:
|
91
85
|
- benedikt@userlist.com
|
@@ -120,6 +114,8 @@ files:
|
|
120
114
|
- lib/userlist/push/resource_collection.rb
|
121
115
|
- lib/userlist/push/serializer.rb
|
122
116
|
- lib/userlist/push/strategies.rb
|
117
|
+
- lib/userlist/push/strategies/active_job.rb
|
118
|
+
- lib/userlist/push/strategies/active_job/worker.rb
|
123
119
|
- lib/userlist/push/strategies/direct.rb
|
124
120
|
- lib/userlist/push/strategies/null.rb
|
125
121
|
- lib/userlist/push/strategies/sidekiq.rb
|
@@ -127,13 +123,15 @@ files:
|
|
127
123
|
- lib/userlist/push/strategies/threaded.rb
|
128
124
|
- lib/userlist/push/strategies/threaded/worker.rb
|
129
125
|
- lib/userlist/push/user.rb
|
126
|
+
- lib/userlist/retryable.rb
|
130
127
|
- lib/userlist/token.rb
|
131
128
|
- lib/userlist/version.rb
|
132
129
|
- userlist.gemspec
|
133
130
|
homepage: http://github.com/userlistio/userlist-ruby
|
134
131
|
licenses:
|
135
132
|
- MIT
|
136
|
-
metadata:
|
133
|
+
metadata:
|
134
|
+
rubygems_mfa_required: 'true'
|
137
135
|
post_install_message:
|
138
136
|
rdoc_options: []
|
139
137
|
require_paths:
|
@@ -149,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
149
147
|
- !ruby/object:Gem::Version
|
150
148
|
version: '0'
|
151
149
|
requirements: []
|
152
|
-
rubygems_version: 3.
|
150
|
+
rubygems_version: 3.4.6
|
153
151
|
signing_key:
|
154
152
|
specification_version: 4
|
155
153
|
summary: Ruby wrapper for the Userlist API
|