salesforce_streamer 0.1.1 → 0.2.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/.gitignore +1 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +5 -5
- data/README.md +37 -20
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/exe/streamer +1 -0
- data/lib/core_extensions/cookiejar/cookie_validation.rb +0 -2
- data/lib/salesforce_streamer/cli.rb +14 -28
- data/lib/salesforce_streamer/configuration.rb +17 -5
- data/lib/salesforce_streamer/errors.rb +1 -1
- data/lib/salesforce_streamer/launcher.rb +7 -8
- data/lib/salesforce_streamer/message_receiver.rb +21 -0
- data/lib/salesforce_streamer/push_topic.rb +11 -3
- data/lib/salesforce_streamer/redis_replay.rb +59 -0
- data/lib/salesforce_streamer/replay_persistence.rb +20 -0
- data/lib/salesforce_streamer/salesforce_client.rb +9 -10
- data/lib/salesforce_streamer/server.rb +3 -5
- data/lib/salesforce_streamer/topic_manager.rb +5 -7
- data/lib/salesforce_streamer/version.rb +1 -1
- data/lib/salesforce_streamer.rb +14 -2
- data/salesforce_streamer.gemspec +22 -21
- metadata +23 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb2a264193dc13244ad48222544789f305e483baf9a632ffeb6917f72056b5a7
|
4
|
+
data.tar.gz: 0bd3ebadf44a12a7adfa56e07cf1fea47dfe390688df85906a4587e188e3b18c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93986c2fdebc68687bf690a87b3cb1dc542685e5a1db839711d0d925ad78a85d9190a44e818788397bc71d98cb038efa63b4e4dd7138045c832f3ddaff8e3245
|
7
|
+
data.tar.gz: c4d9af017d24f23576b03a88fbd6ca637770fda340154cb9d0085f7461994c28a3127a819b26d2b0fa3a2db1fbb5cf97ecfd43d46ba7029d13e81db56220ef8b
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
salesforce_streamer (0.
|
4
|
+
salesforce_streamer (0.2.0)
|
5
5
|
faye (~> 0.8.9)
|
6
6
|
restforce (~> 3.1.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
addressable (2.
|
12
|
-
public_suffix (>= 2.0.2, <
|
11
|
+
addressable (2.7.0)
|
12
|
+
public_suffix (>= 2.0.2, < 5.0)
|
13
13
|
byebug (11.0.1)
|
14
14
|
codecov (0.1.14)
|
15
15
|
json
|
@@ -45,7 +45,7 @@ GEM
|
|
45
45
|
http_parser.rb (0.6.0)
|
46
46
|
json (2.2.0)
|
47
47
|
multipart-post (2.1.1)
|
48
|
-
public_suffix (
|
48
|
+
public_suffix (4.0.1)
|
49
49
|
rack (2.0.7)
|
50
50
|
rake (10.5.0)
|
51
51
|
restforce (3.1.0)
|
@@ -89,4 +89,4 @@ DEPENDENCIES
|
|
89
89
|
salesforce_streamer!
|
90
90
|
|
91
91
|
BUNDLED WITH
|
92
|
-
2.0.
|
92
|
+
2.0.2
|
data/README.md
CHANGED
@@ -45,6 +45,12 @@ base: &DEFAULT
|
|
45
45
|
|
46
46
|
development:
|
47
47
|
<<: *DEFAULT
|
48
|
+
|
49
|
+
test:
|
50
|
+
<<: *DEFAULT
|
51
|
+
|
52
|
+
production:
|
53
|
+
<<: *DEFAULT
|
48
54
|
```
|
49
55
|
|
50
56
|
It's important to note that the way push topics are managed is by the Salesforce
|
@@ -75,29 +81,31 @@ Set your Restforce ENV variables in order to establish a connection. See the
|
|
75
81
|
Restforce API documentation for more details. Then start the server using the
|
76
82
|
command line interface.
|
77
83
|
|
78
|
-
|
84
|
+
Configure the `SalesforceStreamer` module.
|
79
85
|
|
80
|
-
|
81
|
-
|
86
|
+
```ruby
|
87
|
+
# config/initializers/salesforce_streamer.rb
|
82
88
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
89
|
+
require 'redis'
|
90
|
+
require 'connection_pool'
|
91
|
+
|
92
|
+
SalesforceStreamer.config.redis_connection = ConnectionPool.new(size: 5, timeout: 5) { Redis.new }
|
93
|
+
SalesforceStreamer.config.logger = Logger.new(STDERR, level: 'INFO')
|
94
|
+
SalesforceStreamer.config.exception_adapter = proc { |e| puts e }
|
95
|
+
SalesforceStreamer.config.manage_topics = true
|
90
96
|
```
|
91
97
|
|
92
|
-
|
93
|
-
Restforce client logger - not recommended for production.
|
98
|
+
### Launch The Streamer
|
94
99
|
|
95
|
-
|
96
|
-
|
97
|
-
|
100
|
+
Launch the `streamer` service loads the application code at
|
101
|
+
`./config/application` by default if `config.require_path` is unset. It will
|
102
|
+
load your push topic configuration from `./config/streamer.yml` if
|
103
|
+
`config.config_file` is unset. During the boot sequence it will connect to
|
104
|
+
Salesforce using the Restforce client and your configured ENV variables in order
|
105
|
+
to upsert push topic definitions.
|
98
106
|
|
99
107
|
```
|
100
|
-
$ bundle exec streamer
|
108
|
+
$ bundle exec streamer
|
101
109
|
I, [2019-07-08T22:16:34.104271 #26973] INFO -- : Launching Streamer Services
|
102
110
|
I, [2019-07-09T15:19:55.862351 #78537] INFO -- : Running Topic Manager
|
103
111
|
I, [2019-07-09T15:19:56.860998 #78537] INFO -- : New PushTopic AllAccounts
|
@@ -106,14 +114,23 @@ I, [2019-07-09T15:19:56.861109 #78537] INFO -- : Skipping upsert because manage
|
|
106
114
|
I, [2019-07-08T22:16:34.794933 #26973] INFO -- : Starting Server
|
107
115
|
```
|
108
116
|
|
109
|
-
By default, the
|
110
|
-
|
111
|
-
|
117
|
+
By default, the server will start up without syncing the push topic configuration.
|
118
|
+
Set the configuration option `config.manage_topics = true` will tell the server
|
119
|
+
launcher to update the configuration of the push topic in Salesforce.
|
112
120
|
|
113
121
|
```
|
114
|
-
$ bundle exec streamer
|
122
|
+
$ bundle exec streamer
|
123
|
+
I, [2019-07-09T15:19:55.862296 #78537] INFO -- : Launching Streamer Services
|
124
|
+
I, [2019-07-09T15:19:55.862351 #78537] INFO -- : Running Topic Manager
|
125
|
+
I, [2019-07-09T15:19:56.860998 #78537] INFO -- : New PushTopic AllAccounts
|
126
|
+
I, [2019-07-09T15:19:56.861079 #78537] INFO -- : Upsert PushTopic AllAccounts
|
127
|
+
I, [2019-07-09T15:19:57.591241 #78537] INFO -- : Starting Server
|
115
128
|
```
|
116
129
|
|
130
|
+
By default, the executable will load the YAML based on the `RACK_ENV` environment
|
131
|
+
variable, or default to `:development` if not set. You can override this by
|
132
|
+
setting the `config.environment = :integration`
|
133
|
+
|
117
134
|
## Development
|
118
135
|
|
119
136
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'salesforce_streamer'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "salesforce_streamer"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start(__FILE__)
|
data/exe/streamer
CHANGED
@@ -10,7 +10,6 @@ module CoreExtensions
|
|
10
10
|
module CookieValidation
|
11
11
|
def self.extended(base)
|
12
12
|
base.class_eval do
|
13
|
-
|
14
13
|
def self.domains_match(tested_domain, base_domain)
|
15
14
|
return true if tested_domain[-15..-1].eql?('.salesforce.com')
|
16
15
|
|
@@ -21,7 +20,6 @@ module CoreExtensions
|
|
21
20
|
domain == tested_domain
|
22
21
|
end
|
23
22
|
end
|
24
|
-
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
@@ -4,66 +4,52 @@ module SalesforceStreamer
|
|
4
4
|
class CLI
|
5
5
|
def initialize(argv)
|
6
6
|
@argv = argv
|
7
|
-
@config = Configuration.
|
8
|
-
@push_topics_loaded = false
|
7
|
+
@config = Configuration.instance
|
9
8
|
setup_options
|
10
9
|
@parser.parse! @argv
|
11
10
|
end
|
12
11
|
|
13
12
|
def run
|
14
|
-
|
15
|
-
Launcher.new(config: @config).run
|
13
|
+
Launcher.new.run
|
16
14
|
end
|
17
15
|
|
18
16
|
private
|
19
17
|
|
20
|
-
def validate!
|
21
|
-
raise(MissingCLIFlagError, '--config PATH') unless @push_topics_loaded
|
22
|
-
raise(MissingCLIFlagError, '--require PATH') unless @config.require_path
|
23
|
-
rescue MissingCLIFlagError => e
|
24
|
-
puts e
|
25
|
-
puts @parser
|
26
|
-
exit 1
|
27
|
-
end
|
28
|
-
|
29
18
|
def setup_options
|
30
19
|
@parser = OptionParser.new do |o|
|
31
|
-
o.on
|
32
|
-
@config.
|
33
|
-
@push_topics_loaded = true
|
20
|
+
o.on '-C', '--config PATH', 'Load PATH as a config file' do |arg|
|
21
|
+
@config.config_file = arg
|
34
22
|
end
|
35
23
|
|
36
|
-
o.on
|
37
|
-
|
24
|
+
o.on '-e', '--environment ENVIRONMENT',
|
25
|
+
'The environment to run the app on (default development)' do |arg|
|
38
26
|
@config.environment = arg
|
39
27
|
end
|
40
28
|
|
41
|
-
o.on
|
29
|
+
o.on '-r', '--require PATH', 'Load PATH as the entry point to your application' do |arg|
|
42
30
|
@config.require_path = arg
|
43
31
|
end
|
44
32
|
|
45
|
-
o.on
|
33
|
+
o.on '--verbose-restforce', 'Activate the Restforce logger' do
|
46
34
|
@config.restforce_logger!
|
47
35
|
end
|
48
36
|
|
49
|
-
o.on
|
50
|
-
logger = Logger.new(
|
51
|
-
logger.level = arg
|
52
|
-
@config.logger = logger
|
37
|
+
o.on '-v', '--verbose LEVEL', 'Set the log level (default no logging)' do |arg|
|
38
|
+
@config.logger = Logger.new(STDERR, level: arg)
|
53
39
|
end
|
54
40
|
|
55
|
-
o.on
|
41
|
+
o.on '-V', '--version', 'Print the version information' do
|
56
42
|
puts "streamer version #{SalesforceStreamer::VERSION}"
|
57
43
|
exit 0
|
58
44
|
end
|
59
45
|
|
60
|
-
o.on
|
46
|
+
o.on '-x', '--topics', 'Activate PushTopic Management (default off)' do
|
61
47
|
@config.manage_topics = true
|
62
48
|
end
|
63
49
|
|
64
|
-
o.banner =
|
50
|
+
o.banner = 'bundle exec streamer OPTIONS'
|
65
51
|
|
66
|
-
o.on_tail
|
52
|
+
o.on_tail '-h', '--help', 'Show help' do
|
67
53
|
puts o
|
68
54
|
exit 0
|
69
55
|
end
|
@@ -3,22 +3,34 @@
|
|
3
3
|
module SalesforceStreamer
|
4
4
|
# Manages server configuration.
|
5
5
|
class Configuration
|
6
|
-
attr_accessor :environment, :logger, :require_path
|
7
|
-
|
8
|
-
|
6
|
+
attr_accessor :environment, :logger, :require_path, :config_file, :manage_topics, :server, :exception_adapter, :persistence_adapter, :redis_connection
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_writer :instance
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.instance
|
13
|
+
@instance ||= new
|
14
|
+
end
|
9
15
|
|
10
16
|
def initialize
|
11
17
|
@environment = ENV['RACK_ENV'] || :development
|
12
18
|
@logger = Logger.new(IO::NULL)
|
19
|
+
@exception_adapter = proc { |exc| raise exc }
|
20
|
+
@persistence_adapter = RedisReplay.new
|
13
21
|
@manage_topics = false
|
22
|
+
@config_file = './config/streamer.yml'
|
23
|
+
@require_path = './config/application'
|
14
24
|
end
|
15
25
|
|
16
26
|
def manage_topics?
|
17
27
|
@manage_topics
|
18
28
|
end
|
19
29
|
|
20
|
-
def
|
21
|
-
|
30
|
+
def push_topic_data
|
31
|
+
return @push_topic_data if @push_topic_data
|
32
|
+
|
33
|
+
data = YAML.safe_load(File.read(config_file), [], [], true)
|
22
34
|
@push_topic_data = data[environment.to_s]
|
23
35
|
end
|
24
36
|
|
@@ -5,12 +5,11 @@ module SalesforceStreamer
|
|
5
5
|
# Streaming API server. It is responsible for upserting each PushTopic and
|
6
6
|
# starting the server.
|
7
7
|
class Launcher
|
8
|
-
def initialize
|
9
|
-
@
|
10
|
-
@logger = config.logger
|
8
|
+
def initialize
|
9
|
+
@logger = Configuration.instance.logger
|
11
10
|
load_server_configuration
|
12
|
-
@manager = TopicManager.new push_topics: @push_topics
|
13
|
-
@server = Server.new push_topics: @push_topics
|
11
|
+
@manager = TopicManager.new push_topics: @push_topics
|
12
|
+
@server = Server.new push_topics: @push_topics
|
14
13
|
end
|
15
14
|
|
16
15
|
# Manages each PushTopic configured and starts the Streaming API listener.
|
@@ -29,16 +28,16 @@ module SalesforceStreamer
|
|
29
28
|
end
|
30
29
|
|
31
30
|
def require_application
|
32
|
-
if
|
31
|
+
if Configuration.instance.require_path
|
33
32
|
@logger.debug 'Loading the require path'
|
34
|
-
require
|
33
|
+
require Configuration.instance.require_path
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
37
|
def initialize_push_topics
|
39
38
|
@logger.debug 'Loading and validating PushTopics configuration'
|
40
39
|
@push_topics = []
|
41
|
-
|
40
|
+
Configuration.instance.push_topic_data.values.each do |topic_data|
|
42
41
|
@logger.debug topic_data.to_s
|
43
42
|
@push_topics << PushTopic.new(data: topic_data)
|
44
43
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SalesforceStreamer
|
4
|
+
class MessageReceiver
|
5
|
+
class << self
|
6
|
+
# @param topic [String] The unique Salesforce Topic name
|
7
|
+
# @param handler [Object] An object that responds to .call(message)
|
8
|
+
# @param message [Hash] The event payload
|
9
|
+
def call(topic, handler, message)
|
10
|
+
if handler.respond_to? :perform_async
|
11
|
+
handler.perform_async message
|
12
|
+
else
|
13
|
+
handler.call message
|
14
|
+
end
|
15
|
+
ReplayPersistence.record topic, message.dig('event', 'replayId')
|
16
|
+
rescue StandardError => e
|
17
|
+
Configuration.instance.exception_adapter.call e
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -4,12 +4,12 @@ module SalesforceStreamer
|
|
4
4
|
# Models the PushTopic object for both Restforce and Streamer
|
5
5
|
class PushTopic
|
6
6
|
attr_accessor :id
|
7
|
-
attr_reader :name, :
|
8
|
-
|
7
|
+
attr_reader :name, :description, :notify_for_fields, :query,
|
8
|
+
:handler, :handler_constant, :api_version
|
9
9
|
|
10
10
|
def initialize(data:)
|
11
11
|
@handler = data['handler']
|
12
|
-
@
|
12
|
+
@static_replay = data.dig('replay')&.to_i || -1
|
13
13
|
@name = data.dig('salesforce', 'name')
|
14
14
|
@api_version = data.dig('salesforce', 'api_version') || '41.0'
|
15
15
|
@description = data.dig('salesforce', 'description') || @name
|
@@ -18,6 +18,12 @@ module SalesforceStreamer
|
|
18
18
|
validate!
|
19
19
|
end
|
20
20
|
|
21
|
+
def replay
|
22
|
+
@replay ||= (ReplayPersistence.retrieve(name) || @static_replay).tap do |replayId|
|
23
|
+
Configuration.instance.logger.info ['PushTopic name=', name, ' starting at replayId=', replayId].join
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
21
27
|
def to_s
|
22
28
|
"PushTopic id=#{id} name=#{name} handler=#{handler} " \
|
23
29
|
"replay=#{replay} notify_for_fields=#{notify_for_fields} " \
|
@@ -28,6 +34,7 @@ module SalesforceStreamer
|
|
28
34
|
|
29
35
|
def validate!
|
30
36
|
raise(PushTopicNameTooLongError, @name) if @name.size > 25
|
37
|
+
|
31
38
|
@handler_constant = Object.const_get(@handler)
|
32
39
|
true
|
33
40
|
rescue NameError, TypeError => e
|
@@ -37,6 +44,7 @@ module SalesforceStreamer
|
|
37
44
|
|
38
45
|
def strip_spaces(str)
|
39
46
|
raise(NilQueryError, @name) unless str
|
47
|
+
|
40
48
|
str.gsub(/\s+/, ' ')
|
41
49
|
end
|
42
50
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SalesforceStreamer
|
4
|
+
class RedisReplay
|
5
|
+
class << self
|
6
|
+
def redis_connection
|
7
|
+
@redis_connection ||= Configuration.instance.redis_connection || raise(RedisConnectionError)
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_writer :redis_connection
|
11
|
+
end
|
12
|
+
|
13
|
+
def connection
|
14
|
+
if RedisReplay.redis_connection.respond_to?(:with)
|
15
|
+
RedisReplay.redis_connection.with do |conn|
|
16
|
+
yield(conn)
|
17
|
+
end
|
18
|
+
else
|
19
|
+
yield RedisReplay.redis_connection
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Saves the value in a sorted set named by the key
|
24
|
+
# The score is the ReplayId integer value
|
25
|
+
def record(key, value)
|
26
|
+
return unless key && value
|
27
|
+
|
28
|
+
key = namespaced_key(key)
|
29
|
+
value = Integer(value)
|
30
|
+
# The score is the value
|
31
|
+
connection { |c| c.zadd key, value, value }
|
32
|
+
rescue StandardError, TypeError => e
|
33
|
+
Configuration.instance.exception_adapter.call e
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# Retrives the highest value in the sorted set
|
38
|
+
def retrieve(key)
|
39
|
+
return unless key
|
40
|
+
|
41
|
+
key = namespaced_key(key)
|
42
|
+
value = connection { |c| c.zrevrange(key, START, STOP)&.first }
|
43
|
+
Integer(value) if value
|
44
|
+
rescue StandardError => e
|
45
|
+
Configuration.instance.exception_adapter.call e
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def namespaced_key(key)
|
52
|
+
NAMESPACE + key.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
NAMESPACE = 'SalesforceStreamer:'
|
56
|
+
START = 0
|
57
|
+
STOP = 0
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SalesforceStreamer
|
4
|
+
# Store values for a given key in a sorted sequence
|
5
|
+
# Retrieves the highest value given a key
|
6
|
+
class ReplayPersistence
|
7
|
+
class << self
|
8
|
+
def record(key, value)
|
9
|
+
Configuration.instance.logger.debug ['Recording ', key, '=', value].join
|
10
|
+
Configuration.instance.persistence_adapter&.record key, value
|
11
|
+
end
|
12
|
+
|
13
|
+
def retrieve(key)
|
14
|
+
Configuration.instance.persistence_adapter&.retrieve(key).tap do |v|
|
15
|
+
Configuration.instance.logger.debug ['Retrieved for ', key, ' ', v || 'nil'].join
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -25,21 +25,20 @@ module SalesforceStreamer
|
|
25
25
|
# Returns true or raises an exception if the upsert fails
|
26
26
|
def upsert_push_topic(push_topic)
|
27
27
|
@client.upsert!('PushTopic', :Id,
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
)
|
28
|
+
'Id' => push_topic.id,
|
29
|
+
'Name' => push_topic.name,
|
30
|
+
'ApiVersion' => push_topic.api_version,
|
31
|
+
'Description' => push_topic.description,
|
32
|
+
'NotifyForFields' => push_topic.notify_for_fields,
|
33
|
+
'Query' => push_topic.query)
|
35
34
|
end
|
36
35
|
|
37
36
|
private
|
38
37
|
|
39
38
|
QUERY = <<~SOQL.chomp.freeze
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
SELECT Id, Name, ApiVersion, Description, NotifyForFields, Query, isActive
|
40
|
+
FROM PushTopic
|
41
|
+
WHERE Name = '{{NAME}}'
|
43
42
|
SOQL
|
44
43
|
end
|
45
44
|
end
|
@@ -4,8 +4,8 @@ module SalesforceStreamer
|
|
4
4
|
class Server
|
5
5
|
attr_writer :push_topics
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@logger =
|
7
|
+
def initialize(push_topics: [])
|
8
|
+
@logger = Configuration.instance.logger
|
9
9
|
@push_topics = push_topics
|
10
10
|
@client = Restforce.new
|
11
11
|
end
|
@@ -32,9 +32,7 @@ module SalesforceStreamer
|
|
32
32
|
EM.run do
|
33
33
|
@push_topics.map do |topic|
|
34
34
|
@client.subscribe topic.name, replay: topic.replay.to_i do |msg|
|
35
|
-
|
36
|
-
topic.handler_constant.call(msg)
|
37
|
-
@logger.info("Message processed: channel=#{topic.name} replayId=#{msg.dig('event', 'replayId')}")
|
35
|
+
MessageReceiver.call topic.name, topic.handler_constant, msg
|
38
36
|
end
|
39
37
|
end
|
40
38
|
end
|
@@ -4,10 +4,9 @@ module SalesforceStreamer
|
|
4
4
|
class TopicManager
|
5
5
|
attr_reader :push_topics
|
6
6
|
|
7
|
-
def initialize(push_topics
|
7
|
+
def initialize(push_topics:)
|
8
8
|
@push_topics = push_topics
|
9
|
-
@
|
10
|
-
@logger = config.logger
|
9
|
+
@logger = Configuration.instance.logger
|
11
10
|
@client = SalesforceClient.new
|
12
11
|
end
|
13
12
|
|
@@ -15,9 +14,7 @@ module SalesforceStreamer
|
|
15
14
|
@logger.info 'Running Topic Manager'
|
16
15
|
@push_topics.each do |push_topic|
|
17
16
|
@logger.debug push_topic.to_s
|
18
|
-
if diff?(push_topic)
|
19
|
-
upsert(push_topic)
|
20
|
-
end
|
17
|
+
upsert(push_topic) if diff?(push_topic)
|
21
18
|
end
|
22
19
|
end
|
23
20
|
|
@@ -34,13 +31,14 @@ module SalesforceStreamer
|
|
34
31
|
return true unless push_topic.query.eql?(hashie.Query)
|
35
32
|
return true unless push_topic.notify_for_fields.eql?(hashie.NotifyForFields)
|
36
33
|
return true unless push_topic.api_version.to_s.eql?(hashie.ApiVersion.to_s)
|
34
|
+
|
37
35
|
@logger.debug 'No differences detected'
|
38
36
|
false
|
39
37
|
end
|
40
38
|
|
41
39
|
def upsert(push_topic)
|
42
40
|
@logger.info "Upsert PushTopic #{push_topic.name}"
|
43
|
-
if
|
41
|
+
if Configuration.instance.manage_topics?
|
44
42
|
@client.upsert_push_topic(push_topic)
|
45
43
|
else
|
46
44
|
@logger.info 'Skipping upsert because manage topics is off'
|
data/lib/salesforce_streamer.rb
CHANGED
@@ -11,6 +11,9 @@ require 'salesforce_streamer/errors'
|
|
11
11
|
require 'salesforce_streamer/push_topic'
|
12
12
|
require 'salesforce_streamer/topic_manager'
|
13
13
|
require 'salesforce_streamer/salesforce_client'
|
14
|
+
require 'salesforce_streamer/replay_persistence'
|
15
|
+
require 'salesforce_streamer/redis_replay'
|
16
|
+
require 'salesforce_streamer/message_receiver'
|
14
17
|
require 'salesforce_streamer/server'
|
15
18
|
require 'salesforce_streamer/version'
|
16
19
|
require 'salesforce_streamer/launcher'
|
@@ -43,14 +46,23 @@ require 'core_extensions/cookiejar/cookie_validation'
|
|
43
46
|
#
|
44
47
|
# Turn on verbose logging for troubleshooting. The -r flag will activate the
|
45
48
|
# Restforce logger, so this is not recommended for production. The -v flag
|
46
|
-
# activates a Logger to
|
49
|
+
# activates a Logger to STDERR with DEBUG level by default. Set the log level
|
47
50
|
# with the -v flag.
|
48
51
|
#
|
49
|
-
# bundle exec streamer
|
52
|
+
# bundle exec streamer
|
50
53
|
# bundle exec streamer -C config/streamer.yml -v
|
51
54
|
# bundle exec streamer -C config/streamer.yml -v INFO
|
52
55
|
# bundle exec streamer -C config/streamer.yml -r -v
|
53
56
|
# bundle exec streamer -C config/streamer.yml -r -v INFO
|
54
57
|
#
|
55
58
|
module SalesforceStreamer
|
59
|
+
def self.config
|
60
|
+
Configuration.instance
|
61
|
+
end
|
62
|
+
|
63
|
+
class RedisConnectionError < StandardError
|
64
|
+
def initialize
|
65
|
+
super 'SalesforceStreamer.config.redis_connection not set'
|
66
|
+
end
|
67
|
+
end
|
56
68
|
end
|
data/salesforce_streamer.gemspec
CHANGED
@@ -1,44 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
5
|
+
require 'salesforce_streamer/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
+
spec.name = 'salesforce_streamer'
|
8
9
|
spec.version = SalesforceStreamer::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
10
|
+
spec.authors = ['Scott Serok']
|
11
|
+
spec.email = ['scott@renofi.com']
|
11
12
|
|
12
13
|
spec.summary = 'A wrapper around the Restforce Streaming API.'
|
13
14
|
spec.description = 'A wrapper around the Restforce Streaming API.'
|
14
15
|
spec.homepage = 'https://github.com/renofi/salesforce_streamer'
|
15
|
-
spec.license =
|
16
|
+
spec.license = 'MIT'
|
16
17
|
|
17
18
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
19
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
20
|
if spec.respond_to?(:metadata)
|
20
|
-
spec.metadata[
|
21
|
-
spec.metadata[
|
22
|
-
spec.metadata[
|
21
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
22
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
23
|
+
spec.metadata['changelog_uri'] = spec.homepage
|
23
24
|
else
|
24
|
-
raise
|
25
|
-
|
25
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
26
|
+
'public gem pushes.'
|
26
27
|
end
|
27
28
|
|
28
29
|
# Specify which files should be added to the gem when it is released.
|
29
30
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
30
|
-
spec.files
|
31
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
31
32
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
32
33
|
end
|
33
|
-
spec.bindir =
|
34
|
+
spec.bindir = 'exe'
|
34
35
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
35
|
-
spec.require_paths = [
|
36
|
+
spec.require_paths = ['lib']
|
36
37
|
|
37
|
-
spec.add_development_dependency
|
38
|
-
spec.add_development_dependency
|
39
|
-
spec.add_development_dependency
|
40
|
-
spec.add_development_dependency
|
41
|
-
spec.add_development_dependency
|
42
|
-
spec.add_dependency
|
43
|
-
spec.add_dependency
|
38
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
39
|
+
spec.add_development_dependency 'byebug', '~> 11.0.1'
|
40
|
+
spec.add_development_dependency 'codecov', '~> 0.1.14'
|
41
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
42
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
43
|
+
spec.add_dependency 'faye', '~> 0.8.9'
|
44
|
+
spec.add_dependency 'restforce', '~> 3.1.0'
|
44
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: salesforce_streamer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Serok
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -25,89 +25,89 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: byebug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 11.0.1
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 11.0.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: codecov
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.1.14
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.1.14
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: '10.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: '10.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0
|
75
|
+
version: '3.0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0
|
82
|
+
version: '3.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: faye
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 0.8.9
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 0.8.9
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: restforce
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 3.1.0
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 3.1.0
|
111
111
|
description: A wrapper around the Restforce Streaming API.
|
112
112
|
email:
|
113
113
|
- scott@renofi.com
|
@@ -134,7 +134,10 @@ files:
|
|
134
134
|
- lib/salesforce_streamer/configuration.rb
|
135
135
|
- lib/salesforce_streamer/errors.rb
|
136
136
|
- lib/salesforce_streamer/launcher.rb
|
137
|
+
- lib/salesforce_streamer/message_receiver.rb
|
137
138
|
- lib/salesforce_streamer/push_topic.rb
|
139
|
+
- lib/salesforce_streamer/redis_replay.rb
|
140
|
+
- lib/salesforce_streamer/replay_persistence.rb
|
138
141
|
- lib/salesforce_streamer/salesforce_client.rb
|
139
142
|
- lib/salesforce_streamer/server.rb
|
140
143
|
- lib/salesforce_streamer/topic_manager.rb
|