salesforce_streamer 1.2.2 → 2.1.1
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/.dependabot/config.yml +0 -7
- data/CHANGELOG.md +99 -0
- data/Gemfile.lock +26 -26
- data/README.md +62 -48
- data/Rakefile +1 -1
- data/lib/salesforce_streamer.rb +4 -7
- data/lib/salesforce_streamer/cli.rb +1 -1
- data/lib/salesforce_streamer/configuration.rb +19 -9
- data/lib/salesforce_streamer/errors.rb +9 -3
- data/lib/salesforce_streamer/launcher.rb +3 -3
- data/lib/salesforce_streamer/push_topic.rb +31 -36
- data/lib/salesforce_streamer/salesforce_client.rb +2 -4
- data/lib/salesforce_streamer/{topic_manager.rb → salesforce_topic_manager.rb} +7 -6
- data/lib/salesforce_streamer/server.rb +2 -4
- data/lib/salesforce_streamer/version.rb +1 -1
- data/salesforce_streamer.gemspec +4 -2
- metadata +35 -15
- data/lib/salesforce_streamer/redis_replay.rb +0 -55
- data/lib/salesforce_streamer/replay_persistence.rb +0 -18
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 4a92bbc0eaffa8152b8cdcc6b8eb35248a8fe1e996bc73410dc1ed4043acde08
         | 
| 4 | 
            +
              data.tar.gz: '0787479213ab7bc07c07a6cc5d8c9034f5593aee613a28529e25dd51e8c3d9c2'
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 95e9b7ba4133fe7c7840e413677cff3121b6d4c94c9f1037b4fd91fe1a111dd6a1ac350075b987d68cd1fbcb72b733fbed3bd087353b6a075789a8fd15c014cb
         | 
| 7 | 
            +
              data.tar.gz: 573892b3222c3240fb8d171b32f03f446bae3a0ff2184aa7bbbdf71f940ecc961cd84a634dda824a9ecb42acb13446e44b8d0d8f4f4dacb4cae0f1c555658cf0
         | 
    
        data/.dependabot/config.yml
    CHANGED
    
    
    
        data/CHANGELOG.md
    ADDED
    
    | @@ -0,0 +1,99 @@ | |
| 1 | 
            +
            # Changelog
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Sorted so that the most recent change logs are near the top. Only significant
         | 
| 4 | 
            +
            changes are logged in the change log.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            ## 2020-08-17 Scott Serok [scott@renofi.com](mailto:scott@renofi.com)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            v2.1 changes the expected interface of `Configuration#replay_adapter`.
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            Normally this breaking change would require a major version bump, but since the
         | 
| 11 | 
            +
            functionality today is quiet broken we can call this a major bug fix.
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            The `config.replay_adapter` should be an object that has an interface like Hash.
         | 
| 14 | 
            +
            It must respond to `[]` and `[]=`. By default the adapter is an empty hash.  If
         | 
| 15 | 
            +
            you want your push topic replayId to persist between restarts, then you should
         | 
| 16 | 
            +
            implement a class with an appropriate interface.
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            ```ruby
         | 
| 19 | 
            +
            class MyReplayAdapter
         | 
| 20 | 
            +
              def [](channel)
         | 
| 21 | 
            +
                MyPersistence.get(channel)
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def []=(channel, replay_id)
         | 
| 25 | 
            +
                MyPersistence.set(channel, replay_id)
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
| 28 | 
            +
            ```
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            This change was sparked by a misunderstanding of the
         | 
| 31 | 
            +
            `Restforce::Concerns::Streaming::ReplayExtension` replay handlers.
         | 
| 32 | 
            +
            SalesforceStreamer can eliminate some complexity and fix a bug by delegating the
         | 
| 33 | 
            +
            responsibility of maintaining the current replayId to that ReplayExtension. The
         | 
| 34 | 
            +
            object will be used on each request/response cycle to record and read the latest
         | 
| 35 | 
            +
            replayId as long as the object assigned to `config.replay_adapter` responds to
         | 
| 36 | 
            +
            `[]` and `[]=`.
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            ## 2020-08-04 Scott Serok [scott@renofi.com](mailto:scott@renofi.com)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            v2.0 is released as a major simplification of this library. There are 2
         | 
| 41 | 
            +
            significant differences from a user's perspective.
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            1. The YAML configuration requires minor edits to be compatible in v2.0.
         | 
| 44 | 
            +
            2. The built-in Redis persistance of the replayId field has been removed. You
         | 
| 45 | 
            +
               should add a custom middleware and configure the new replay_adapter option.
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            ### PushTopic configuration changes
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            After upgrading to v2, the YAML configuration should be modified. Shift the
         | 
| 50 | 
            +
            nested "salesforce" block to the left and remove the "salesforce" key.
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            Before v2:
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                name: "TopicName"
         | 
| 55 | 
            +
                handler: "MyConstant"
         | 
| 56 | 
            +
                salesforce:
         | 
| 57 | 
            +
                  query: "SELECT Id FROM Lead"
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            As of v2:
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                name: "TopicName"
         | 
| 62 | 
            +
                handler: "MyConstant"
         | 
| 63 | 
            +
                query: "SELECT Id FROM Lead"
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            ### Redis Persistance removed
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            The original intention of this library is to manage PushTopic definitions
         | 
| 68 | 
            +
            and run an event machine that subscribes to the Salesforce Streaming API based
         | 
| 69 | 
            +
            on those PushTopics.
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            The addition of managing the Replay ID adds unecessary complexity that can be
         | 
| 72 | 
            +
            incorporated through customization, and so it's been removed. You might use a
         | 
| 73 | 
            +
            recent commit of v1 of this library for reference how to implement Redis as the
         | 
| 74 | 
            +
            persistence layer.
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            To record the replayId on every message we can add a piece of middleware
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                class RecordReplayIdMiddleware
         | 
| 79 | 
            +
                  def initialize(handler)
         | 
| 80 | 
            +
                    @handler = handler
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  def call(message)
         | 
| 84 | 
            +
                    @handler.call(message)
         | 
| 85 | 
            +
                    replay_id = message['event']['replayId']
         | 
| 86 | 
            +
                    topic_name = message['topic']
         | 
| 87 | 
            +
                    MyStore.record_replay_id(replay_id, topic_name)
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
                SalesforceStreamer.config.use_middleware RecordReplayIdMiddleware
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            To retrieve the replayId before subscribing to a PushTopic,
         | 
| 93 | 
            +
            configure an adapter that returns an integer.
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                SalesforceStreamer.config.replay_adapter = proc { |topic|
         | 
| 96 | 
            +
                  (MyStore.fetch_replay_id(topic.name) || -1).to_i
         | 
| 97 | 
            +
                }
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            This will be used to set the replayId value when subscribing to the PushTopic.
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,9 +1,10 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                salesforce_streamer (1. | 
| 5 | 
            -
                   | 
| 6 | 
            -
                   | 
| 4 | 
            +
                salesforce_streamer (2.1.1)
         | 
| 5 | 
            +
                  dry-initializer (~> 3.0)
         | 
| 6 | 
            +
                  faye (~> 1.4)
         | 
| 7 | 
            +
                  restforce (>= 4.2, < 6.0)
         | 
| 7 8 |  | 
| 8 9 | 
             
            GEM
         | 
| 9 10 | 
             
              remote: https://rubygems.org/
         | 
| @@ -12,15 +13,14 @@ GEM | |
| 12 13 | 
             
                  public_suffix (>= 2.0.2, < 5.0)
         | 
| 13 14 | 
             
                ast (2.4.1)
         | 
| 14 15 | 
             
                byebug (11.1.3)
         | 
| 15 | 
            -
                codecov (0.2. | 
| 16 | 
            -
                  colorize
         | 
| 16 | 
            +
                codecov (0.2.9)
         | 
| 17 17 | 
             
                  json
         | 
| 18 18 | 
             
                  simplecov
         | 
| 19 | 
            -
                colorize (0.8.1)
         | 
| 20 19 | 
             
                cookiejar (0.3.3)
         | 
| 21 20 | 
             
                diff-lcs (1.4.4)
         | 
| 22 21 | 
             
                docile (1.3.2)
         | 
| 23 | 
            -
                 | 
| 22 | 
            +
                dry-initializer (3.0.3)
         | 
| 23 | 
            +
                em-http-request (1.1.7)
         | 
| 24 24 | 
             
                  addressable (>= 2.3.4)
         | 
| 25 25 | 
             
                  cookiejar (!= 0.3.1)
         | 
| 26 26 | 
             
                  em-socksify (>= 0.3)
         | 
| @@ -29,24 +29,26 @@ GEM | |
| 29 29 | 
             
                em-socksify (0.3.2)
         | 
| 30 30 | 
             
                  eventmachine (>= 1.0.0.beta.4)
         | 
| 31 31 | 
             
                eventmachine (1.2.7)
         | 
| 32 | 
            -
                faraday (1.0. | 
| 32 | 
            +
                faraday (1.0.1)
         | 
| 33 33 | 
             
                  multipart-post (>= 1.2, < 3)
         | 
| 34 34 | 
             
                faraday_middleware (1.0.0)
         | 
| 35 35 | 
             
                  faraday (~> 1.0)
         | 
| 36 | 
            -
                faye ( | 
| 36 | 
            +
                faye (1.4.0)
         | 
| 37 37 | 
             
                  cookiejar (>= 0.3.0)
         | 
| 38 | 
            -
                  em-http-request (>=  | 
| 38 | 
            +
                  em-http-request (>= 1.1.6)
         | 
| 39 39 | 
             
                  eventmachine (>= 0.12.0)
         | 
| 40 | 
            -
                  faye-websocket (>= 0. | 
| 40 | 
            +
                  faye-websocket (>= 0.11.0)
         | 
| 41 | 
            +
                  multi_json (>= 1.0.0)
         | 
| 41 42 | 
             
                  rack (>= 1.0.0)
         | 
| 42 | 
            -
                   | 
| 43 | 
            +
                  websocket-driver (>= 0.5.1)
         | 
| 43 44 | 
             
                faye-websocket (0.11.0)
         | 
| 44 45 | 
             
                  eventmachine (>= 0.12.0)
         | 
| 45 46 | 
             
                  websocket-driver (>= 0.5.1)
         | 
| 46 47 | 
             
                hashie (4.1.0)
         | 
| 47 48 | 
             
                http_parser.rb (0.6.0)
         | 
| 48 49 | 
             
                json (2.3.1)
         | 
| 49 | 
            -
                jwt (2.2. | 
| 50 | 
            +
                jwt (2.2.2)
         | 
| 51 | 
            +
                multi_json (1.15.0)
         | 
| 50 52 | 
             
                multipart-post (2.1.1)
         | 
| 51 53 | 
             
                parallel (1.19.2)
         | 
| 52 54 | 
             
                parser (2.7.1.4)
         | 
| @@ -56,11 +58,10 @@ GEM | |
| 56 58 | 
             
                rainbow (3.0.0)
         | 
| 57 59 | 
             
                rake (13.0.1)
         | 
| 58 60 | 
             
                regexp_parser (1.7.1)
         | 
| 59 | 
            -
                restforce ( | 
| 60 | 
            -
                  faraday (>= 0.9.0, <=  | 
| 61 | 
            -
                  faraday_middleware (>= 0.8.8, <=  | 
| 61 | 
            +
                restforce (5.0.1)
         | 
| 62 | 
            +
                  faraday (>= 0.9.0, <= 2.0)
         | 
| 63 | 
            +
                  faraday_middleware (>= 0.8.8, <= 2.0)
         | 
| 62 64 | 
             
                  hashie (>= 1.2.0, < 5.0)
         | 
| 63 | 
            -
                  json (>= 1.7.5)
         | 
| 64 65 | 
             
                  jwt (>= 1.5.6)
         | 
| 65 66 | 
             
                rexml (3.2.4)
         | 
| 66 67 | 
             
                rspec (3.9.0)
         | 
| @@ -76,23 +77,23 @@ GEM | |
| 76 77 | 
             
                  diff-lcs (>= 1.2.0, < 2.0)
         | 
| 77 78 | 
             
                  rspec-support (~> 3.9.0)
         | 
| 78 79 | 
             
                rspec-support (3.9.3)
         | 
| 79 | 
            -
                rubocop (0. | 
| 80 | 
            +
                rubocop (0.90.0)
         | 
| 80 81 | 
             
                  parallel (~> 1.10)
         | 
| 81 82 | 
             
                  parser (>= 2.7.1.1)
         | 
| 82 83 | 
             
                  rainbow (>= 2.2.2, < 4.0)
         | 
| 83 84 | 
             
                  regexp_parser (>= 1.7)
         | 
| 84 85 | 
             
                  rexml
         | 
| 85 | 
            -
                  rubocop-ast (>= 0. | 
| 86 | 
            +
                  rubocop-ast (>= 0.3.0, < 1.0)
         | 
| 86 87 | 
             
                  ruby-progressbar (~> 1.7)
         | 
| 87 88 | 
             
                  unicode-display_width (>= 1.4.0, < 2.0)
         | 
| 88 | 
            -
                rubocop-ast (0. | 
| 89 | 
            -
                  parser (>= 2.7. | 
| 90 | 
            -
                rubocop-performance (1.7. | 
| 89 | 
            +
                rubocop-ast (0.3.0)
         | 
| 90 | 
            +
                  parser (>= 2.7.1.4)
         | 
| 91 | 
            +
                rubocop-performance (1.7.1)
         | 
| 91 92 | 
             
                  rubocop (>= 0.82.0)
         | 
| 92 | 
            -
                rubocop-rspec (1. | 
| 93 | 
            -
                  rubocop ( | 
| 93 | 
            +
                rubocop-rspec (1.43.2)
         | 
| 94 | 
            +
                  rubocop (~> 0.87)
         | 
| 94 95 | 
             
                ruby-progressbar (1.10.1)
         | 
| 95 | 
            -
                simplecov (0. | 
| 96 | 
            +
                simplecov (0.19.0)
         | 
| 96 97 | 
             
                  docile (~> 1.1)
         | 
| 97 98 | 
             
                  simplecov-html (~> 0.11)
         | 
| 98 99 | 
             
                simplecov-html (0.12.2)
         | 
| @@ -100,7 +101,6 @@ GEM | |
| 100 101 | 
             
                websocket-driver (0.7.3)
         | 
| 101 102 | 
             
                  websocket-extensions (>= 0.1.0)
         | 
| 102 103 | 
             
                websocket-extensions (0.1.5)
         | 
| 103 | 
            -
                yajl-ruby (1.4.1)
         | 
| 104 104 |  | 
| 105 105 | 
             
            PLATFORMS
         | 
| 106 106 | 
             
              ruby
         | 
    
        data/README.md
    CHANGED
    
    | @@ -20,33 +20,11 @@ And then execute: | |
| 20 20 |  | 
| 21 21 | 
             
            ## Usage
         | 
| 22 22 |  | 
| 23 | 
            -
            ### Middleware
         | 
| 24 | 
            -
             | 
| 25 | 
            -
            You can initialize the streamer server with any number of middleware classes.
         | 
| 26 | 
            -
            When a message is received by a PushTopic subscription, the chain of middleware
         | 
| 27 | 
            -
            classes are executed before the message handler is called.
         | 
| 28 | 
            -
             | 
| 29 | 
            -
            ```ruby
         | 
| 30 | 
            -
            # config/initializers/streamer.rb
         | 
| 31 | 
            -
            class MySimpleMiddleware
         | 
| 32 | 
            -
              def initialize(handler)
         | 
| 33 | 
            -
                @handler = handler
         | 
| 34 | 
            -
              end
         | 
| 35 | 
            -
              def call(message)
         | 
| 36 | 
            -
                @handler.call(message)
         | 
| 37 | 
            -
              end
         | 
| 38 | 
            -
            end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
            SalesforceStreamer.config.use_middleware MySimpleMiddleware
         | 
| 41 | 
            -
            ```
         | 
| 42 | 
            -
             | 
| 43 23 | 
             
            ### Configure Push Topics
         | 
| 44 24 |  | 
| 45 | 
            -
            Create a YAML file to configure your  | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
            line. For more information about the `replay:` and `notify_fields_for` options
         | 
| 49 | 
            -
            please see the Salesforce Streaming API reference documentation.
         | 
| 25 | 
            +
            Create a YAML file to configure your PushTopic subscriptions. When streamer
         | 
| 26 | 
            +
            starts up it will check for any differences between Salesforce PushTopics and
         | 
| 27 | 
            +
            this yaml and update any differences when `config.manage_topics = true`.
         | 
| 50 28 |  | 
| 51 29 | 
             
            ```yaml
         | 
| 52 30 | 
             
            # config/streamer.yml
         | 
| @@ -55,12 +33,11 @@ base: &DEFAULT | |
| 55 33 | 
             
              accounts:
         | 
| 56 34 | 
             
                handler: "AccountChangeHandler"
         | 
| 57 35 | 
             
                replay: -1
         | 
| 58 | 
            -
                 | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
                  query: "Select Id, Name From Account"
         | 
| 36 | 
            +
                name: "AllAccounts"
         | 
| 37 | 
            +
                api_version: "49.0"
         | 
| 38 | 
            +
                description: "Sync Accounts"
         | 
| 39 | 
            +
                notify_for_fields: "Referenced"
         | 
| 40 | 
            +
                query: "Select Id, Name From Account"
         | 
| 64 41 |  | 
| 65 42 | 
             
            development:
         | 
| 66 43 | 
             
              <<: *DEFAULT
         | 
| @@ -72,14 +49,6 @@ production: | |
| 72 49 | 
             
              <<: *DEFAULT
         | 
| 73 50 | 
             
            ```
         | 
| 74 51 |  | 
| 75 | 
            -
            It's important to note that the way push topics are managed is by the Salesforce
         | 
| 76 | 
            -
            name attribute.  This should uniquely identify each push topic.  It is not
         | 
| 77 | 
            -
            recommended to change the name of your push topic definitions; otherwise, the
         | 
| 78 | 
            -
            push topic manager will not find a push topic in Salesforce resulting in the
         | 
| 79 | 
            -
            creation of a brand new push topic. If the push topic manager identifies a
         | 
| 80 | 
            -
            difference in any of the other Salesforce attributes, then it will update the
         | 
| 81 | 
            -
            push topic in Salesforce before starting the streaming server.
         | 
| 82 | 
            -
             | 
| 83 52 | 
             
            ### Define Message Handlers
         | 
| 84 53 |  | 
| 85 54 | 
             
            Define your handlers somewhere in your project. They must respond to either
         | 
| @@ -89,14 +58,17 @@ Define your handlers somewhere in your project. They must respond to either | |
| 89 58 | 
             
            # lib/account_change_handler.rb
         | 
| 90 59 | 
             
            # Handle account changes inline
         | 
| 91 60 | 
             
            class AccountChangeHandler
         | 
| 92 | 
            -
               | 
| 93 | 
            -
                 | 
| 61 | 
            +
              class << self
         | 
| 62 | 
            +
                def call(message)
         | 
| 63 | 
            +
                  puts message
         | 
| 64 | 
            +
                end
         | 
| 94 65 | 
             
              end
         | 
| 95 66 | 
             
            end
         | 
| 96 67 |  | 
| 97 68 | 
             
            # Handle account changes asynchronously
         | 
| 98 69 | 
             
            class AccountChangeHandler
         | 
| 99 70 | 
             
              include Sidekiq::Worker
         | 
| 71 | 
            +
             | 
| 100 72 | 
             
              def perform(message)
         | 
| 101 73 | 
             
                puts message
         | 
| 102 74 | 
             
              end
         | 
| @@ -114,13 +86,13 @@ Configure the `SalesforceStreamer` module. | |
| 114 86 | 
             
            ```ruby
         | 
| 115 87 | 
             
            # config/initializers/salesforce_streamer.rb
         | 
| 116 88 |  | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 89 | 
            +
            SalesforceStreamer.configure do |config|
         | 
| 90 | 
            +
              config.logger = Logger.new(STDERR, level: 'INFO')
         | 
| 91 | 
            +
              config.exception_adapter = proc { |e| puts e }
         | 
| 92 | 
            +
              config.replay_adapter = MyReplayAdapter
         | 
| 93 | 
            +
              config.use_middleware AfterMessageReceived
         | 
| 94 | 
            +
              config.manage_topics = true
         | 
| 95 | 
            +
            end
         | 
| 124 96 | 
             
            ```
         | 
| 125 97 |  | 
| 126 98 | 
             
            ### Launch The Streamer
         | 
| @@ -159,6 +131,48 @@ By default, the executable will load the YAML based on the `RACK_ENV` environmen | |
| 159 131 | 
             
            variable, or default to `:development` if not set. You can override this by
         | 
| 160 132 | 
             
            setting the `config.environment = :integration`
         | 
| 161 133 |  | 
| 134 | 
            +
            ### Message Handling Middleware
         | 
| 135 | 
            +
             | 
| 136 | 
            +
            You can initialize the streamer server with any number of middleware classes.
         | 
| 137 | 
            +
            When a message is received by a PushTopic subscription, the chain of middleware
         | 
| 138 | 
            +
            classes are executed before the message handler is called.
         | 
| 139 | 
            +
             | 
| 140 | 
            +
            ```ruby
         | 
| 141 | 
            +
            # config/initializers/streamer.rb
         | 
| 142 | 
            +
            class MySimpleMiddleware
         | 
| 143 | 
            +
              def initialize(handler)
         | 
| 144 | 
            +
                @handler = handler
         | 
| 145 | 
            +
              end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
              def call(message)
         | 
| 148 | 
            +
                @handler.call(message)
         | 
| 149 | 
            +
              end
         | 
| 150 | 
            +
            end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
            SalesforceStreamer.config.use_middleware MySimpleMiddleware
         | 
| 153 | 
            +
            ```
         | 
| 154 | 
            +
             | 
| 155 | 
            +
            ### ReplayAdapter
         | 
| 156 | 
            +
             | 
| 157 | 
            +
            The `config.replay_adapter` should be an object that has an interface like Hash.
         | 
| 158 | 
            +
            It must respond to `[]` and `[]=`. By default the adapter is an empty hash.  If
         | 
| 159 | 
            +
            you want your push topic replayId to persist between restarts, then you should
         | 
| 160 | 
            +
            implement a class with an appropriate interface.
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            ```ruby
         | 
| 163 | 
            +
            class MyReplayAdapter
         | 
| 164 | 
            +
              def [](channel)
         | 
| 165 | 
            +
                Persistence.get(channel)
         | 
| 166 | 
            +
              end
         | 
| 167 | 
            +
             | 
| 168 | 
            +
              def []=(channel, replay_id)
         | 
| 169 | 
            +
                Persistence.set(channel, replay_id)
         | 
| 170 | 
            +
              end
         | 
| 171 | 
            +
            end
         | 
| 172 | 
            +
            ```
         | 
| 173 | 
            +
             | 
| 174 | 
            +
            This adapter will be used directly by `Restforce::ReplayExtension`.
         | 
| 175 | 
            +
             | 
| 162 176 | 
             
            ## Development
         | 
| 163 177 |  | 
| 164 178 | 
             
            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/lib/salesforce_streamer.rb
    CHANGED
    
    | @@ -4,16 +4,15 @@ require 'optparse' | |
| 4 4 | 
             
            require 'restforce'
         | 
| 5 5 | 
             
            require 'yaml'
         | 
| 6 6 | 
             
            require 'forwardable'
         | 
| 7 | 
            +
            require 'dry-initializer'
         | 
| 7 8 |  | 
| 8 9 | 
             
            require 'salesforce_streamer/configuration'
         | 
| 9 10 | 
             
            require 'salesforce_streamer/errors'
         | 
| 10 11 | 
             
            require 'salesforce_streamer/replay_id_error_extension'
         | 
| 11 12 | 
             
            require 'salesforce_streamer/log'
         | 
| 12 13 | 
             
            require 'salesforce_streamer/push_topic'
         | 
| 13 | 
            -
            require 'salesforce_streamer/ | 
| 14 | 
            +
            require 'salesforce_streamer/salesforce_topic_manager'
         | 
| 14 15 | 
             
            require 'salesforce_streamer/salesforce_client'
         | 
| 15 | 
            -
            require 'salesforce_streamer/replay_persistence'
         | 
| 16 | 
            -
            require 'salesforce_streamer/redis_replay'
         | 
| 17 16 | 
             
            require 'salesforce_streamer/server'
         | 
| 18 17 | 
             
            require 'salesforce_streamer/version'
         | 
| 19 18 | 
             
            require 'salesforce_streamer/launcher'
         | 
| @@ -60,9 +59,7 @@ module SalesforceStreamer | |
| 60 59 | 
             
                Configuration.instance
         | 
| 61 60 | 
             
              end
         | 
| 62 61 |  | 
| 63 | 
            -
               | 
| 64 | 
            -
                 | 
| 65 | 
            -
                  super 'SalesforceStreamer.config.redis_connection not set'
         | 
| 66 | 
            -
                end
         | 
| 62 | 
            +
              def self.configure
         | 
| 63 | 
            +
                yield Configuration.instance
         | 
| 67 64 | 
             
              end
         | 
| 68 65 | 
             
            end
         | 
| @@ -32,7 +32,7 @@ module SalesforceStreamer | |
| 32 32 | 
             
                    end
         | 
| 33 33 |  | 
| 34 34 | 
             
                    o.on '-v', '--verbose LEVEL', 'Set the log level (default no logging)' do |arg|
         | 
| 35 | 
            -
                      @config.logger = Logger.new( | 
| 35 | 
            +
                      @config.logger = Logger.new($stderr, level: arg)
         | 
| 36 36 | 
             
                    end
         | 
| 37 37 |  | 
| 38 38 | 
             
                    o.on '-V', '--version', 'Print the version information' do
         | 
| @@ -1,22 +1,27 @@ | |
| 1 1 | 
             
            module SalesforceStreamer
         | 
| 2 2 | 
             
              # Manages server configuration.
         | 
| 3 3 | 
             
              class Configuration
         | 
| 4 | 
            -
                attr_accessor :environment, :logger, :require_path, :config_file, | 
| 5 | 
            -
                  : | 
| 4 | 
            +
                attr_accessor :environment, :logger, :require_path, :config_file,
         | 
| 5 | 
            +
                  :manage_topics, :exception_adapter, :replay_adapter
         | 
| 6 | 
            +
                attr_reader :middleware
         | 
| 6 7 |  | 
| 7 8 | 
             
                class << self
         | 
| 8 9 | 
             
                  attr_writer :instance
         | 
| 9 | 
            -
                end
         | 
| 10 10 |  | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 11 | 
            +
                  def configure
         | 
| 12 | 
            +
                    yield instance
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def instance
         | 
| 16 | 
            +
                    @instance ||= new
         | 
| 17 | 
            +
                  end
         | 
| 13 18 | 
             
                end
         | 
| 14 19 |  | 
| 15 20 | 
             
                def initialize
         | 
| 16 21 | 
             
                  @environment = ENV['RACK_ENV'] || :development
         | 
| 17 22 | 
             
                  @logger = Logger.new(IO::NULL)
         | 
| 18 23 | 
             
                  @exception_adapter = proc { |exc| fail(exc) }
         | 
| 19 | 
            -
                  @ | 
| 24 | 
            +
                  @replay_adapter = Hash.new { |hash, key| hash[key] = -1 }
         | 
| 20 25 | 
             
                  @manage_topics = false
         | 
| 21 26 | 
             
                  @config_file = './config/streamer.yml'
         | 
| 22 27 | 
             
                  @require_path = './config/environment'
         | 
| @@ -27,12 +32,17 @@ module SalesforceStreamer | |
| 27 32 | 
             
                  @manage_topics
         | 
| 28 33 | 
             
                end
         | 
| 29 34 |  | 
| 35 | 
            +
                # adds a setup proc to the middleware array
         | 
| 30 36 | 
             
                def use_middleware(klass, *args, &block)
         | 
| 31 | 
            -
                  middleware <<  | 
| 37 | 
            +
                  @middleware << [klass, args, block]
         | 
| 32 38 | 
             
                end
         | 
| 33 39 |  | 
| 34 | 
            -
                 | 
| 35 | 
            -
             | 
| 40 | 
            +
                # returns a ready to use chain of middleware
         | 
| 41 | 
            +
                def middleware_runner(last_handler)
         | 
| 42 | 
            +
                  @middleware.reduce(last_handler) do |next_handler, current_handler|
         | 
| 43 | 
            +
                    klass, args, block = current_handler
         | 
| 44 | 
            +
                    klass.new(next_handler, *args, &block)
         | 
| 45 | 
            +
                  end
         | 
| 36 46 | 
             
                end
         | 
| 37 47 |  | 
| 38 48 | 
             
                def push_topic_data
         | 
| @@ -3,13 +3,13 @@ module SalesforceStreamer | |
| 3 3 |  | 
| 4 4 | 
             
              class MissingCLIFlagError < StandardError
         | 
| 5 5 | 
             
                def initialize(flag)
         | 
| 6 | 
            -
                  super  | 
| 6 | 
            +
                  super "Missing required command line flag: #{flag}"
         | 
| 7 7 | 
             
                end
         | 
| 8 8 | 
             
              end
         | 
| 9 9 |  | 
| 10 10 | 
             
              class NilQueryError < StandardError
         | 
| 11 11 | 
             
                def initialize(name)
         | 
| 12 | 
            -
                  super  | 
| 12 | 
            +
                  super "Query not defined for #{name}"
         | 
| 13 13 | 
             
                end
         | 
| 14 14 | 
             
              end
         | 
| 15 15 |  | 
| @@ -21,7 +21,13 @@ module SalesforceStreamer | |
| 21 21 |  | 
| 22 22 | 
             
              class PushTopicNameTooLongError < StandardError
         | 
| 23 23 | 
             
                def initialize(name)
         | 
| 24 | 
            -
                  super  | 
| 24 | 
            +
                  super "PushTopic name: #{name} (#{name.size}/25)"
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              class UnprocessableHandlerError < StandardError
         | 
| 29 | 
            +
                def initialize(constant)
         | 
| 30 | 
            +
                  super "#{constant} does not repond to .call or .perform_async"
         | 
| 25 31 | 
             
                end
         | 
| 26 32 | 
             
              end
         | 
| 27 33 | 
             
            end
         | 
| @@ -5,14 +5,14 @@ module SalesforceStreamer | |
| 5 5 | 
             
              class Launcher
         | 
| 6 6 | 
             
                def initialize
         | 
| 7 7 | 
             
                  load_server_configuration
         | 
| 8 | 
            -
                  @manager =  | 
| 8 | 
            +
                  @manager = SalesforceTopicManager.new push_topics: @push_topics
         | 
| 9 9 | 
             
                  @server = Server.new push_topics: @push_topics
         | 
| 10 10 | 
             
                end
         | 
| 11 11 |  | 
| 12 12 | 
             
                # Manages each PushTopic configured and starts the Streaming API listener.
         | 
| 13 13 | 
             
                def run
         | 
| 14 14 | 
             
                  Log.info 'Launching Streamer Services'
         | 
| 15 | 
            -
                  @manager. | 
| 15 | 
            +
                  @manager.upsert_topics!
         | 
| 16 16 | 
             
                  @server.push_topics = @manager.push_topics
         | 
| 17 17 | 
             
                  @server.run
         | 
| 18 18 | 
             
                end
         | 
| @@ -36,7 +36,7 @@ module SalesforceStreamer | |
| 36 36 | 
             
                  @push_topics = []
         | 
| 37 37 | 
             
                  Configuration.instance.push_topic_data.each_value do |topic_data|
         | 
| 38 38 | 
             
                    Log.debug topic_data.to_s
         | 
| 39 | 
            -
                    @push_topics << PushTopic.new( | 
| 39 | 
            +
                    @push_topics << PushTopic.new(topic_data.transform_keys(&:to_sym))
         | 
| 40 40 | 
             
                  end
         | 
| 41 41 | 
             
                end
         | 
| 42 42 | 
             
              end
         | 
| @@ -1,38 +1,29 @@ | |
| 1 1 | 
             
            module SalesforceStreamer
         | 
| 2 2 | 
             
              # Models the PushTopic object for both Restforce and Streamer
         | 
| 3 3 | 
             
              class PushTopic
         | 
| 4 | 
            -
                 | 
| 5 | 
            -
                attr_reader :name, :description, :notify_for_fields, :query,
         | 
| 6 | 
            -
                  :handler, :handler_constant, :api_version
         | 
| 4 | 
            +
                extend Dry::Initializer
         | 
| 7 5 |  | 
| 8 | 
            -
                 | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
                  validate!
         | 
| 17 | 
            -
                end
         | 
| 6 | 
            +
                option :name
         | 
| 7 | 
            +
                option :query, proc { |str| str.gsub(/\s+/, ' ') }
         | 
| 8 | 
            +
                option :handler, proc { |str| prepare_handler_proc Object.const_get(str) }
         | 
| 9 | 
            +
                option :replay, proc(&:to_i), default: proc { -1 }
         | 
| 10 | 
            +
                option :api_version, proc(&:to_s), default: proc { '49.0' }
         | 
| 11 | 
            +
                option :notify_for_fields, default: proc { 'Referenced' }
         | 
| 12 | 
            +
                option :id, optional: true
         | 
| 13 | 
            +
                option :description, optional: true
         | 
| 18 14 |  | 
| 19 | 
            -
                 | 
| 20 | 
            -
                  ReplayPersistence.retrieve(name) || @static_replay
         | 
| 21 | 
            -
                end
         | 
| 15 | 
            +
                attr_writer :id
         | 
| 22 16 |  | 
| 23 17 | 
             
                def handle(message)
         | 
| 24 18 | 
             
                  message['topic'] = @name
         | 
| 25 | 
            -
                   | 
| 26 | 
            -
                  ReplayPersistence.record @name, message.dig('event', 'replayId')
         | 
| 19 | 
            +
                  message_middleware.call(message)
         | 
| 27 20 | 
             
                rescue StandardError => e
         | 
| 28 21 | 
             
                  Log.error e
         | 
| 29 22 | 
             
                  Configuration.instance.exception_adapter.call e
         | 
| 30 23 | 
             
                end
         | 
| 31 24 |  | 
| 32 | 
            -
                def  | 
| 33 | 
            -
                   | 
| 34 | 
            -
                    "replay=#{replay} notify_for_fields=#{notify_for_fields} " \
         | 
| 35 | 
            -
                    "description=#{description} api_version=#{api_version} query=#{query}"
         | 
| 25 | 
            +
                def attributes
         | 
| 26 | 
            +
                  self.class.dry_initializer.public_attributes self
         | 
| 36 27 | 
             
                end
         | 
| 37 28 |  | 
| 38 29 | 
             
                private
         | 
| @@ -40,29 +31,33 @@ module SalesforceStreamer | |
| 40 31 | 
             
                def validate!
         | 
| 41 32 | 
             
                  fail(PushTopicNameTooLongError, @name) if @name.size > 25
         | 
| 42 33 |  | 
| 43 | 
            -
                  @ | 
| 34 | 
            +
                  @handler = Object.const_get(@handler)
         | 
| 44 35 | 
             
                  true
         | 
| 45 36 | 
             
                rescue NameError, TypeError => e
         | 
| 46 | 
            -
                  message =  | 
| 37 | 
            +
                  message = "handler=#{@handler} exception=#{e}"
         | 
| 47 38 | 
             
                  raise(PushTopicHandlerMissingError, message)
         | 
| 48 39 | 
             
                end
         | 
| 49 40 |  | 
| 50 | 
            -
                def  | 
| 51 | 
            -
                  Configuration.instance. | 
| 41 | 
            +
                def message_middleware
         | 
| 42 | 
            +
                  Configuration.instance.middleware_runner(handler)
         | 
| 52 43 | 
             
                end
         | 
| 53 44 |  | 
| 54 | 
            -
                 | 
| 55 | 
            -
                   | 
| 56 | 
            -
                     | 
| 57 | 
            -
                  else
         | 
| 58 | 
            -
                    handler_constant
         | 
| 59 | 
            -
                  end
         | 
| 60 | 
            -
                end
         | 
| 45 | 
            +
                class << self
         | 
| 46 | 
            +
                  def strip_spaces(str)
         | 
| 47 | 
            +
                    fail(NilQueryError, @name) unless str
         | 
| 61 48 |  | 
| 62 | 
            -
             | 
| 63 | 
            -
                   | 
| 49 | 
            +
                    str.gsub(/\s+/, ' ')
         | 
| 50 | 
            +
                  end
         | 
| 64 51 |  | 
| 65 | 
            -
                   | 
| 52 | 
            +
                  def prepare_handler_proc(constant)
         | 
| 53 | 
            +
                    if constant.respond_to? :call
         | 
| 54 | 
            +
                      constant
         | 
| 55 | 
            +
                    elsif constant.respond_to? :perform_async
         | 
| 56 | 
            +
                      proc { |message| constant.perform_async message }
         | 
| 57 | 
            +
                    else
         | 
| 58 | 
            +
                      fail(UnprocessableHandlerError, constant)
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
                  end
         | 
| 66 61 | 
             
                end
         | 
| 67 62 | 
             
              end
         | 
| 68 63 | 
             
            end
         | 
| @@ -8,10 +8,8 @@ module SalesforceStreamer | |
| 8 8 | 
             
                  @client.authenticate!
         | 
| 9 9 | 
             
                end
         | 
| 10 10 |  | 
| 11 | 
            -
                def subscribe(*args)
         | 
| 12 | 
            -
                  @client.subscribe(args) | 
| 13 | 
            -
                    yield
         | 
| 14 | 
            -
                  end
         | 
| 11 | 
            +
                def subscribe(*args, &block)
         | 
| 12 | 
            +
                  @client.subscribe(args, &block)
         | 
| 15 13 | 
             
                end
         | 
| 16 14 |  | 
| 17 15 | 
             
                # Returns nil or an instance of Restforce::SObject
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            module SalesforceStreamer
         | 
| 2 | 
            -
              class  | 
| 2 | 
            +
              class SalesforceTopicManager
         | 
| 3 3 | 
             
                attr_reader :push_topics
         | 
| 4 4 |  | 
| 5 5 | 
             
                def initialize(push_topics:)
         | 
| @@ -7,10 +7,11 @@ module SalesforceStreamer | |
| 7 7 | 
             
                  @client = SalesforceClient.new
         | 
| 8 8 | 
             
                end
         | 
| 9 9 |  | 
| 10 | 
            -
                def  | 
| 11 | 
            -
                  Log.info ' | 
| 10 | 
            +
                def upsert_topics!
         | 
| 11 | 
            +
                  Log.info 'Starting to upsert PushTopic definitions into Salesforce'
         | 
| 12 12 | 
             
                  @push_topics.each do |push_topic|
         | 
| 13 | 
            -
                    Log. | 
| 13 | 
            +
                    Log.info push_topic.name
         | 
| 14 | 
            +
                    Log.debug push_topic.attributes.to_json
         | 
| 14 15 | 
             
                    upsert(push_topic) if diff?(push_topic)
         | 
| 15 16 | 
             
                  end
         | 
| 16 17 | 
             
                end
         | 
| @@ -29,12 +30,12 @@ module SalesforceStreamer | |
| 29 30 | 
             
                  return true unless push_topic.notify_for_fields.eql?(hashie.NotifyForFields)
         | 
| 30 31 | 
             
                  return true unless push_topic.api_version.to_s.eql?(hashie.ApiVersion.to_s)
         | 
| 31 32 |  | 
| 32 | 
            -
                  Log. | 
| 33 | 
            +
                  Log.info 'No differences detected'
         | 
| 33 34 | 
             
                  false
         | 
| 34 35 | 
             
                end
         | 
| 35 36 |  | 
| 36 37 | 
             
                def upsert(push_topic)
         | 
| 37 | 
            -
                  Log.info " | 
| 38 | 
            +
                  Log.info "Upserting PushTopic"
         | 
| 38 39 | 
             
                  if Configuration.instance.manage_topics?
         | 
| 39 40 | 
             
                    @client.upsert_push_topic(push_topic)
         | 
| 40 41 | 
             
                  else
         | 
| @@ -34,11 +34,9 @@ module SalesforceStreamer | |
| 34 34 | 
             
                def start_em
         | 
| 35 35 | 
             
                  EM.run do
         | 
| 36 36 | 
             
                    @push_topics.map do |topic|
         | 
| 37 | 
            -
                      client.subscribe topic.name, replay:  | 
| 38 | 
            -
                         | 
| 39 | 
            -
                        Log.info "Message #{replay_id} received from topic #{topic.name}"
         | 
| 37 | 
            +
                      client.subscribe topic.name, replay: Configuration.instance.replay_adapter do |message|
         | 
| 38 | 
            +
                        Log.info "Message #{message.dig('event', 'replayId')} received from topic #{topic.name}"
         | 
| 40 39 | 
             
                        topic.handle message
         | 
| 41 | 
            -
                        topic.id = replay_id
         | 
| 42 40 | 
             
                      end
         | 
| 43 41 | 
             
                    end
         | 
| 44 42 | 
             
                  end
         | 
    
        data/salesforce_streamer.gemspec
    CHANGED
    
    | @@ -14,6 +14,7 @@ Gem::Specification.new do |spec| | |
| 14 14 |  | 
| 15 15 | 
             
              spec.metadata['homepage_uri'] = spec.homepage
         | 
| 16 16 | 
             
              spec.metadata['source_code_uri'] = spec.homepage
         | 
| 17 | 
            +
              spec.metadata['documentation_uri'] = 'https://www.rubydoc.info/gems/salesforce_streamer'
         | 
| 17 18 |  | 
| 18 19 | 
             
              spec.files = Dir.chdir(File.expand_path(__dir__)) do
         | 
| 19 20 | 
             
                `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(bin/|spec/|\.rub)}) }
         | 
| @@ -24,8 +25,9 @@ Gem::Specification.new do |spec| | |
| 24 25 |  | 
| 25 26 | 
             
              spec.required_ruby_version = '>= 2.6'
         | 
| 26 27 |  | 
| 27 | 
            -
              spec.add_dependency ' | 
| 28 | 
            -
              spec.add_dependency ' | 
| 28 | 
            +
              spec.add_dependency 'dry-initializer', '~> 3.0'
         | 
| 29 | 
            +
              spec.add_dependency 'faye', '~> 1.4'
         | 
| 30 | 
            +
              spec.add_dependency 'restforce', '>= 4.2', '< 6.0'
         | 
| 29 31 |  | 
| 30 32 | 
             
              spec.add_development_dependency 'byebug'
         | 
| 31 33 | 
             
              spec.add_development_dependency 'codecov'
         | 
    
        metadata
    CHANGED
    
    | @@ -1,44 +1,64 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: salesforce_streamer
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 2.1.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Scott Serok
         | 
| 8 8 | 
             
            - RenoFi Engineering Team
         | 
| 9 | 
            -
            autorequire: | 
| 9 | 
            +
            autorequire:
         | 
| 10 10 | 
             
            bindir: exe
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2020- | 
| 12 | 
            +
            date: 2020-09-02 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 15 | 
            +
              name: dry-initializer
         | 
| 16 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 17 | 
            +
                requirements:
         | 
| 18 | 
            +
                - - "~>"
         | 
| 19 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 20 | 
            +
                    version: '3.0'
         | 
| 21 | 
            +
              type: :runtime
         | 
| 22 | 
            +
              prerelease: false
         | 
| 23 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 24 | 
            +
                requirements:
         | 
| 25 | 
            +
                - - "~>"
         | 
| 26 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 27 | 
            +
                    version: '3.0'
         | 
| 14 28 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 29 | 
             
              name: faye
         | 
| 16 30 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 17 31 | 
             
                requirements:
         | 
| 18 | 
            -
                - -  | 
| 32 | 
            +
                - - "~>"
         | 
| 19 33 | 
             
                  - !ruby/object:Gem::Version
         | 
| 20 | 
            -
                    version:  | 
| 34 | 
            +
                    version: '1.4'
         | 
| 21 35 | 
             
              type: :runtime
         | 
| 22 36 | 
             
              prerelease: false
         | 
| 23 37 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 24 38 | 
             
                requirements:
         | 
| 25 | 
            -
                - -  | 
| 39 | 
            +
                - - "~>"
         | 
| 26 40 | 
             
                  - !ruby/object:Gem::Version
         | 
| 27 | 
            -
                    version:  | 
| 41 | 
            +
                    version: '1.4'
         | 
| 28 42 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 29 43 | 
             
              name: restforce
         | 
| 30 44 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 31 45 | 
             
                requirements:
         | 
| 32 | 
            -
                - - " | 
| 46 | 
            +
                - - ">="
         | 
| 33 47 | 
             
                  - !ruby/object:Gem::Version
         | 
| 34 48 | 
             
                    version: '4.2'
         | 
| 49 | 
            +
                - - "<"
         | 
| 50 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 51 | 
            +
                    version: '6.0'
         | 
| 35 52 | 
             
              type: :runtime
         | 
| 36 53 | 
             
              prerelease: false
         | 
| 37 54 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 38 55 | 
             
                requirements:
         | 
| 39 | 
            -
                - - " | 
| 56 | 
            +
                - - ">="
         | 
| 40 57 | 
             
                  - !ruby/object:Gem::Version
         | 
| 41 58 | 
             
                    version: '4.2'
         | 
| 59 | 
            +
                - - "<"
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '6.0'
         | 
| 42 62 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 43 63 | 
             
              name: byebug
         | 
| 44 64 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -137,7 +157,7 @@ dependencies: | |
| 137 157 | 
             
                - - ">="
         | 
| 138 158 | 
             
                  - !ruby/object:Gem::Version
         | 
| 139 159 | 
             
                    version: '0'
         | 
| 140 | 
            -
            description: | 
| 160 | 
            +
            description:
         | 
| 141 161 | 
             
            email:
         | 
| 142 162 | 
             
            - scott@renofi.com
         | 
| 143 163 | 
             
            - engineering@renofi.com
         | 
| @@ -155,6 +175,7 @@ files: | |
| 155 175 | 
             
            - ".gitignore"
         | 
| 156 176 | 
             
            - ".rspec"
         | 
| 157 177 | 
             
            - ".travis.yml"
         | 
| 178 | 
            +
            - CHANGELOG.md
         | 
| 158 179 | 
             
            - Gemfile
         | 
| 159 180 | 
             
            - Gemfile.lock
         | 
| 160 181 | 
             
            - LICENSE.txt
         | 
| @@ -169,12 +190,10 @@ files: | |
| 169 190 | 
             
            - lib/salesforce_streamer/launcher.rb
         | 
| 170 191 | 
             
            - lib/salesforce_streamer/log.rb
         | 
| 171 192 | 
             
            - lib/salesforce_streamer/push_topic.rb
         | 
| 172 | 
            -
            - lib/salesforce_streamer/redis_replay.rb
         | 
| 173 193 | 
             
            - lib/salesforce_streamer/replay_id_error_extension.rb
         | 
| 174 | 
            -
            - lib/salesforce_streamer/replay_persistence.rb
         | 
| 175 194 | 
             
            - lib/salesforce_streamer/salesforce_client.rb
         | 
| 195 | 
            +
            - lib/salesforce_streamer/salesforce_topic_manager.rb
         | 
| 176 196 | 
             
            - lib/salesforce_streamer/server.rb
         | 
| 177 | 
            -
            - lib/salesforce_streamer/topic_manager.rb
         | 
| 178 197 | 
             
            - lib/salesforce_streamer/version.rb
         | 
| 179 198 | 
             
            - salesforce_streamer.gemspec
         | 
| 180 199 | 
             
            homepage: https://github.com/renofi/salesforce_streamer
         | 
| @@ -183,7 +202,8 @@ licenses: | |
| 183 202 | 
             
            metadata:
         | 
| 184 203 | 
             
              homepage_uri: https://github.com/renofi/salesforce_streamer
         | 
| 185 204 | 
             
              source_code_uri: https://github.com/renofi/salesforce_streamer
         | 
| 186 | 
            -
             | 
| 205 | 
            +
              documentation_uri: https://www.rubydoc.info/gems/salesforce_streamer
         | 
| 206 | 
            +
            post_install_message:
         | 
| 187 207 | 
             
            rdoc_options: []
         | 
| 188 208 | 
             
            require_paths:
         | 
| 189 209 | 
             
            - lib
         | 
| @@ -199,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 199 219 | 
             
                  version: '0'
         | 
| 200 220 | 
             
            requirements: []
         | 
| 201 221 | 
             
            rubygems_version: 3.1.2
         | 
| 202 | 
            -
            signing_key: | 
| 222 | 
            +
            signing_key:
         | 
| 203 223 | 
             
            specification_version: 4
         | 
| 204 224 | 
             
            summary: A wrapper around the Restforce Streaming API with a built-in PushTopic manager.
         | 
| 205 225 | 
             
            test_files: []
         | 
| @@ -1,55 +0,0 @@ | |
| 1 | 
            -
            module SalesforceStreamer
         | 
| 2 | 
            -
              class RedisReplay
         | 
| 3 | 
            -
                class << self
         | 
| 4 | 
            -
                  def redis_connection
         | 
| 5 | 
            -
                    @redis_connection ||= Configuration.instance.redis_connection || fail(RedisConnectionError)
         | 
| 6 | 
            -
                  end
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                  attr_writer :redis_connection
         | 
| 9 | 
            -
                end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                def connection
         | 
| 12 | 
            -
                  if RedisReplay.redis_connection.respond_to?(:with)
         | 
| 13 | 
            -
                    RedisReplay.redis_connection.with do |conn|
         | 
| 14 | 
            -
                      yield(conn)
         | 
| 15 | 
            -
                    end
         | 
| 16 | 
            -
                  else
         | 
| 17 | 
            -
                    yield RedisReplay.redis_connection
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
                end
         | 
| 20 | 
            -
             | 
| 21 | 
            -
                # Saves the value to a key with expiration
         | 
| 22 | 
            -
                def record(key, value)
         | 
| 23 | 
            -
                  return unless key && value
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                  key = namespaced_key(key)
         | 
| 26 | 
            -
                  value = Integer(value)
         | 
| 27 | 
            -
                  connection { |c| c.setex key, SECONDS_TO_EXPIRE, value }
         | 
| 28 | 
            -
                rescue StandardError => e
         | 
| 29 | 
            -
                  Configuration.instance.exception_adapter.call e
         | 
| 30 | 
            -
                  nil
         | 
| 31 | 
            -
                end
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                def retrieve(key)
         | 
| 34 | 
            -
                  return unless key
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                  key = namespaced_key(key)
         | 
| 37 | 
            -
                  value = connection { |c| c.get key }
         | 
| 38 | 
            -
                  Integer(value) if value
         | 
| 39 | 
            -
                rescue StandardError => e
         | 
| 40 | 
            -
                  Configuration.instance.exception_adapter.call e
         | 
| 41 | 
            -
                  nil
         | 
| 42 | 
            -
                end
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                private
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                def namespaced_key(key)
         | 
| 47 | 
            -
                  NAMESPACE + key.to_s
         | 
| 48 | 
            -
                end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                NAMESPACE = 'SalesforceStreamer:'.freeze
         | 
| 51 | 
            -
                SECONDS_TO_EXPIRE = 24 * 60 * 60 # 24 hours
         | 
| 52 | 
            -
                START = 0
         | 
| 53 | 
            -
                STOP = 0
         | 
| 54 | 
            -
              end
         | 
| 55 | 
            -
            end
         | 
| @@ -1,18 +0,0 @@ | |
| 1 | 
            -
            module SalesforceStreamer
         | 
| 2 | 
            -
              # Store values for a given key in a sorted sequence
         | 
| 3 | 
            -
              # Retrieves the highest value given a key
         | 
| 4 | 
            -
              class ReplayPersistence
         | 
| 5 | 
            -
                class << self
         | 
| 6 | 
            -
                  def record(key, value)
         | 
| 7 | 
            -
                    Log.debug { "Recording #{key}=#{value}" }
         | 
| 8 | 
            -
                    Configuration.instance.persistence_adapter&.record key, value
         | 
| 9 | 
            -
                  end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                  def retrieve(key)
         | 
| 12 | 
            -
                    Configuration.instance.persistence_adapter&.retrieve(key).tap do |v|
         | 
| 13 | 
            -
                      Log.debug { "Retrieved for #{key} #{v || 'nil'}" }
         | 
| 14 | 
            -
                    end
         | 
| 15 | 
            -
                  end
         | 
| 16 | 
            -
                end
         | 
| 17 | 
            -
              end
         | 
| 18 | 
            -
            end
         |