salesforce_streamer 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b58d79963e641ea50df2ecf51308df7ab452aa508c7e4833535723fac79488c3
4
- data.tar.gz: 4078e279b38b7ace14a5cbdb567d513bfe4d7fc4a55cc84690c2f81ffb8a9e64
3
+ metadata.gz: bb2a264193dc13244ad48222544789f305e483baf9a632ffeb6917f72056b5a7
4
+ data.tar.gz: 0bd3ebadf44a12a7adfa56e07cf1fea47dfe390688df85906a4587e188e3b18c
5
5
  SHA512:
6
- metadata.gz: 5b3bf235265594cad4930d3fbf127cb1416dac8023f293ddb22b75d7f2a98f799e6d8ae60718728e9009b644320eab3cd7c0eea6129b192cc4f7cdd2672d0c4f
7
- data.tar.gz: b9570a7f0d0b6a6cbbaed04a209227b8e7cbd6ed9dbd2daacfbdc2b5227e2b40f46679d479d5025df387c4b861772393499fd9cda168573979ec29a7adad1cca
6
+ metadata.gz: 93986c2fdebc68687bf690a87b3cb1dc542685e5a1db839711d0d925ad78a85d9190a44e818788397bc71d98cb038efa63b4e4dd7138045c832f3ddaff8e3245
7
+ data.tar.gz: c4d9af017d24f23576b03a88fbd6ca637770fda340154cb9d0085f7461994c28a3127a819b26d2b0fa3a2db1fbb5cf97ecfd43d46ba7029d13e81db56220ef8b
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+ .env
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in salesforce_streamer.gemspec
4
6
  gemspec
data/Gemfile.lock CHANGED
@@ -1,15 +1,15 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- salesforce_streamer (0.1.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.6.0)
12
- public_suffix (>= 2.0.2, < 4.0)
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 (3.1.1)
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.1
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
- ### Launch The Streamer
84
+ Configure the `SalesforceStreamer` module.
79
85
 
80
- Launch the `streamer` service, pointing it to your push topic configuration YAML
81
- file and the entry point to your application.
86
+ ```ruby
87
+ # config/initializers/salesforce_streamer.rb
82
88
 
83
- ```
84
- $ bundle exec streamer -C config/streamer.yml -r ./lib/app -x -v INFO
85
- I, [2019-07-09T15:19:55.862296 #78537] INFO -- : Launching Streamer Services
86
- I, [2019-07-09T15:19:55.862351 #78537] INFO -- : Running Topic Manager
87
- I, [2019-07-09T15:19:56.860998 #78537] INFO -- : New PushTopic AllAccounts
88
- I, [2019-07-09T15:19:56.861079 #78537] INFO -- : Upsert PushTopic AllAccounts
89
- I, [2019-07-09T15:19:57.591241 #78537] INFO -- : Starting Server
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
- For very verbose logs, also use the `--verbose-restforce` flag to activate the
93
- Restforce client logger - not recommended for production.
98
+ ### Launch The Streamer
94
99
 
95
- You can start up the server without syncing the push topic configuration if you
96
- know the topics are already configured appropriately. Remove the `-x` flag from
97
- the CLI to skip the topic management component.
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 -C config/streamer.yml -r ./lib/app -v INFO
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 CLI will load the YAML based on the `RACK_ENV` environment
110
- variable, or default to `:development` if not set. You can override this in the
111
- CLI with the `-e ENV` flag.
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 -C config/streamer.yml -r ./lib/app -e production
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
@@ -1,6 +1,8 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "salesforce_streamer"
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 "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
data/exe/streamer CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'salesforce_streamer'
4
5
  require 'salesforce_streamer/cli'
@@ -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.new
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
- validate!
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 "-C", "--config PATH", "Load PATH as a config file" do |arg|
32
- @config.load_push_topic_data arg
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 "-e", "--environment ENVIRONMENT",
37
- "The environment to run the app on (default development)" do |arg|
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 "-r", "--require PATH", "Load PATH as the entry point to your application" do |arg|
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 "--verbose-restforce", "Activate the Restforce logger" do
33
+ o.on '--verbose-restforce', 'Activate the Restforce logger' do
46
34
  @config.restforce_logger!
47
35
  end
48
36
 
49
- o.on "-v", "--verbose LEVEL", "Set the log level (default no logging)" do |arg|
50
- logger = Logger.new(STDOUT)
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 "-V", "--version", "Print the version information" do
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 "-x", "--topics", "Activate PushTopic Management (default off)" do
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 = "streamer OPTIONS"
50
+ o.banner = 'bundle exec streamer OPTIONS'
65
51
 
66
- o.on_tail "-h", "--help", "Show help" do
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
- attr_reader :push_topic_data
8
- attr_writer :manage_topics
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 load_push_topic_data(path)
21
- data = YAML.safe_load(File.read(path), [], [], true)
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
 
@@ -15,7 +15,7 @@ module SalesforceStreamer
15
15
 
16
16
  class PushTopicHandlerMissingError < StandardError
17
17
  def initialize(message)
18
- super "Unable to load constant #{message.to_s}."
18
+ super "Unable to load constant #{message}."
19
19
  end
20
20
  end
21
21
 
@@ -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(config:)
9
- @config = config
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, config: @config
13
- @server = Server.new push_topics: @push_topics, config: @config
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 @config.require_path
31
+ if Configuration.instance.require_path
33
32
  @logger.debug 'Loading the require path'
34
- require @config.require_path
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
- @config.push_topic_data.values.each do |topic_data|
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, :replay, :description, :notify_for_fields, :query,
8
- :handler, :handler_constant, :api_version
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
- @replay = data.dig('replay')&.to_i || -1
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
- '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
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
- SELECT Id, Name, ApiVersion, Description, NotifyForFields, Query, isActive
41
- FROM PushTopic
42
- WHERE Name = '{{NAME}}'
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(config:, push_topics: [])
8
- @logger = config.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
- @logger.debug(msg)
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:, config:)
7
+ def initialize(push_topics:)
8
8
  @push_topics = push_topics
9
- @config = config
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 @config.manage_topics?
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'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SalesforceStreamer
4
- VERSION = '0.1.1'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -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 STDOUT with DEBUG level by default. Set the log level
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 -C config/streamer.yml -r
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
@@ -1,44 +1,45 @@
1
+ # frozen_string_literal: true
1
2
 
2
- lib = File.expand_path("../lib", __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "salesforce_streamer/version"
5
+ require 'salesforce_streamer/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "salesforce_streamer"
8
+ spec.name = 'salesforce_streamer'
8
9
  spec.version = SalesforceStreamer::VERSION
9
- spec.authors = ["Scott Serok"]
10
- spec.email = ["scott@renofi.com"]
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 = "MIT"
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["homepage_uri"] = spec.homepage
21
- spec.metadata["source_code_uri"] = spec.homepage
22
- spec.metadata["changelog_uri"] = spec.homepage
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 "RubyGems 2.0 or newer is required to protect against " \
25
- "public gem pushes."
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 = Dir.chdir(File.expand_path('..', __FILE__)) do
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 = "exe"
34
+ spec.bindir = 'exe'
34
35
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
- spec.require_paths = ["lib"]
36
+ spec.require_paths = ['lib']
36
37
 
37
- spec.add_development_dependency "bundler", "~> 2.0"
38
- spec.add_development_dependency "rake", "~> 10.0"
39
- spec.add_development_dependency "rspec", "~> 3.0"
40
- spec.add_development_dependency "byebug", "~> 11.0.1"
41
- spec.add_development_dependency "codecov", "~> 0.1.14"
42
- spec.add_dependency "restforce", "~> 3.1.0"
43
- spec.add_dependency "faye", "~> 0.8.9"
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.1.1
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-07-17 00:00:00.000000000 Z
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: rake
28
+ name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
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: '10.0'
40
+ version: 11.0.1
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: codecov
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
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: '3.0'
54
+ version: 0.1.14
55
55
  - !ruby/object:Gem::Dependency
56
- name: byebug
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 11.0.1
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: 11.0.1
68
+ version: '10.0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: codecov
70
+ name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.1.14
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.1.14
82
+ version: '3.0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: restforce
84
+ name: faye
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 3.1.0
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: 3.1.0
96
+ version: 0.8.9
97
97
  - !ruby/object:Gem::Dependency
98
- name: faye
98
+ name: restforce
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 0.8.9
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: 0.8.9
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