{"action":"created","comment":{"url":"https://api.github.com/repos/ejholmes/restforce/pulls/comments/104797207","pull_request_review_id":25649813,"id":104797207,"diff_hunk":"@@ -30,6 +31,65 @@ def faye\n           client.bind 'transport:up' do\n             Restforce.log \"[COMETD UP]\"\n           end\n+\n+          client.add_extension ReplayExtension.new(replay_handlers)\n+        end\n+      end\n+\n+      def replay_handlers\n+        @_replay_handlers ||= {}\n+      end\n+\n+      class ReplayExtension\n+        def initialize(replay_handlers)\n+          @replay_handlers = replay_handlers\n+        end\n+\n+        def incoming(message, callback)\n+          channel = message.fetch('channel').gsub('/topic/', '')\n+          replay_id = message.fetch('data', {}).fetch('event', {})['replayId']","path":"lib/restforce/concerns/streaming.rb","position":30,"original_position":30,"commit_id":"808aa3cada2f181711e72b16fffa381a592e533b","original_commit_id":"808aa3cada2f181711e72b16fffa381a592e533b","user":{"login":"drteeth","id":376404,"avatar_url":"https://avatars2.githubusercontent.com/u/376404?v=3","gravatar_id":"","url":"https://api.github.com/users/drteeth","html_url":"https://github.com/drteeth","followers_url":"https://api.github.com/users/drteeth/followers","following_url":"https://api.github.com/users/drteeth/following{/other_user}","gists_url":"https://api.github.com/users/drteeth/gists{/gist_id}","starred_url":"https://api.github.com/users/drteeth/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/drteeth/subscriptions","organizations_url":"https://api.github.com/users/drteeth/orgs","repos_url":"https://api.github.com/users/drteeth/repos","events_url":"https://api.github.com/users/drteeth/events{/privacy}","received_events_url":"https://api.github.com/users/drteeth/received_events","type":"User","site_admin":false},"body":"Good idea. I'll make the change soon.","created_at":"2017-03-07T22:28:19Z","updated_at":"2017-03-07T22:28:20Z","html_url":"https://github.com/ejholmes/restforce/pull/265#discussion_r104797207","pull_request_url":"https://api.github.com/repos/ejholmes/restforce/pulls/265","_links":{"self":{"href":"https://api.github.com/repos/ejholmes/restforce/pulls/comments/104797207"},"html":{"href":"https://github.com/ejholmes/restforce/pull/265#discussion_r104797207"},"pull_request":{"href":"https://api.github.com/repos/ejholmes/restforce/pulls/265"}}},"pull_request":{"url":"https://api.github.com/repos/ejholmes/restforce/pulls/265","id":90320809,"html_url":"https://github.com/ejholmes/restforce/pull/265","diff_url":"https://github.com/ejholmes/restforce/pull/265.diff","patch_url":"https://github.com/ejholmes/restforce/pull/265.patch","issue_url":"https://api.github.com/repos/ejholmes/restforce/issues/265","number":265,"state":"open","locked":false,"title":"Implement push topic replay","user":{"login":"panozzaj","id":51120,"avatar_url":"https://avatars3.githubusercontent.com/u/51120?v=3","gravatar_id":"","url":"https://api.github.com/users/panozzaj","html_url":"https://github.com/panozzaj","followers_url":"https://api.github.com/users/panozzaj/followers","following_url":"https://api.github.com/users/panozzaj/following{/other_user}","gists_url":"https://api.github.com/users/panozzaj/gists{/gist_id}","starred_url":"https://api.github.com/users/panozzaj/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/panozzaj/subscriptions","organizations_url":"https://api.github.com/users/panozzaj/orgs","repos_url":"https://api.github.com/users/panozzaj/repos","events_url":"https://api.github.com/users/panozzaj/events{/privacy}","received_events_url":"https://api.github.com/users/panozzaj/received_events","type":"User","site_admin":false},"body":"Thanks to the existing code in Restforce, I was able to get up and running with the PushTopic streaming API in just a few lines of code. We have used this library on a couple of projects now.\n\nFor PushTopics, Salesforce added [the ability to replay messages](https://developer.salesforce.com/docs/atlas.en-us.api_streaming.meta/api_streaming/using_streaming_api_durability.htm) (see #237). This PR adds an options hash to the `subscribe` method on clients, which allows us to pass a message id to replay from using a client extension. With this code, you can replay messages:\n\n```\nclient.subscribe push_topic_name, replay: 7 do |message|\n  # message handler here\nend\n```\n\nThis will replay message 8 and up (based on Salesforce's documentation of this feature.) I have verified the functionality with a local script that points to this PR's version of the gem.\n\n---\n\nI am pushing this up for initial feedback. I think it needs at least a little more work.\n\nOne thing is that the current design does not let you easily subscribe to some channels with a replay id, and subsequently subscribe to others with no replay id. This is because there is only one Faye client initialized in total (it is cached between `subscribe`s.)\n\nI think we might be able to remove the extension by storing it and then removing it later if we subscribe with no replay id, but it seems a little clunky. I wanted to see what the intent of the `faye` method is or why we want to only create one Faye client in the existing code. It probably didn't matter before since we were never modifying the client, but now that we are, it is a little more complicated. I am guessing the method was made public just for testing purposes based on the associated test and a search of the code, but wasn't sure of the intent of it. It seems like if we made a new client for each call to `subscribe` that we could easily pass in replay id or not and not have any interference between calls.\n\nAdditionally, I'd like to add tests and documentation for this feature and clean up the the logging (it's a bit verbose.) I'd also be curious to know if there is a more preferred place to put the `ReplayExtension` class, since I just lumped it in to the existing streaming module file.\n\n---\n\nNote that if you try to replay a message that is already expired (was created 24 hours or more ago), you will get the following error when attempting to subscribe to the channel:\n\n```\n#<Faye::Error:0x007fe9361df548 @code=nil, @params=[], @message=\"400::The replayId {2} you provided was invalid.  Please provide a valid ID, -2 to replay all events, or -1 to replay only new events.\">\n```\n\nThe best solution would seem to be to either increase the number you are replaying until you find a good one, or to use \"-2\" to replay all possible messages that Salesforce still has.\n\n---\n\nUseful resources:\n- [cometdReplayExtension.js](https://github.com/developerforce/StreamingReplayClientExtensions/blob/48ce54bb9ef939ff13c73302a0f33786c5c343f2/javascript/cometdReplayExtension.js) (this gave a lot of detail that was useful)\n- [Faye client extensions documentation](https://faye.jcoglan.com/ruby/extensions.html) (pretty much used this exact template for the extension)\n- [Faye Ruby client example](https://github.com/faye/faye/blob/master/examples/ruby/client.rb)\n","created_at":"2016-10-21T04:19:36Z","updated_at":"2017-03-07T22:28:20Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5ac18e88b43104dfe50ec1a4f5bcb19b56d7afbc","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/ejholmes/restforce/pulls/265/commits","review_comments_url":"https://api.github.com/repos/ejholmes/restforce/pulls/265/comments","review_comment_url":"https://api.github.com/repos/ejholmes/restforce/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ejholmes/restforce/issues/265/comments","statuses_url":"https://api.github.com/repos/ejholmes/restforce/statuses/808aa3cada2f181711e72b16fffa381a592e533b","head":{"label":"OurHealth:pushtopic-replay","ref":"pushtopic-replay","sha":"808aa3cada2f181711e72b16fffa381a592e533b","user":{"login":"OurHealth","id":2955664,"avatar_url":"https://avatars3.githubusercontent.com/u/2955664?v=3","gravatar_id":"","url":"https://api.github.com/users/OurHealth","html_url":"https://github.com/OurHealth","followers_url":"https://api.github.com/users/OurHealth/followers","following_url":"https://api.github.com/users/OurHealth/following{/other_user}","gists_url":"https://api.github.com/users/OurHealth/gists{/gist_id}","starred_url":"https://api.github.com/users/OurHealth/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OurHealth/subscriptions","organizations_url":"https://api.github.com/users/OurHealth/orgs","repos_url":"https://api.github.com/users/OurHealth/repos","events_url":"https://api.github.com/users/OurHealth/events{/privacy}","received_events_url":"https://api.github.com/users/OurHealth/received_events","type":"Organization","site_admin":false},"repo":{"id":71527404,"name":"restforce","full_name":"OurHealth/restforce","owner":{"login":"OurHealth","id":2955664,"avatar_url":"https://avatars3.githubusercontent.com/u/2955664?v=3","gravatar_id":"","url":"https://api.github.com/users/OurHealth","html_url":"https://github.com/OurHealth","followers_url":"https://api.github.com/users/OurHealth/followers","following_url":"https://api.github.com/users/OurHealth/following{/other_user}","gists_url":"https://api.github.com/users/OurHealth/gists{/gist_id}","starred_url":"https://api.github.com/users/OurHealth/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OurHealth/subscriptions","organizations_url":"https://api.github.com/users/OurHealth/orgs","repos_url":"https://api.github.com/users/OurHealth/repos","events_url":"https://api.github.com/users/OurHealth/events{/privacy}","received_events_url":"https://api.github.com/users/OurHealth/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/OurHealth/restforce","description":"A ruby client for the Salesforce REST api.","fork":true,"url":"https://api.github.com/repos/OurHealth/restforce","forks_url":"https://api.github.com/repos/OurHealth/restforce/forks","keys_url":"https://api.github.com/repos/OurHealth/restforce/keys{/key_id}","collaborators_url":"https://api.github.com/repos/OurHealth/restforce/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/OurHealth/restforce/teams","hooks_url":"https://api.github.com/repos/OurHealth/restforce/hooks","issue_events_url":"https://api.github.com/repos/OurHealth/restforce/issues/events{/number}","events_url":"https://api.github.com/repos/OurHealth/restforce/events","assignees_url":"https://api.github.com/repos/OurHealth/restforce/assignees{/user}","branches_url":"https://api.github.com/repos/OurHealth/restforce/branches{/branch}","tags_url":"https://api.github.com/repos/OurHealth/restforce/tags","blobs_url":"https://api.github.com/repos/OurHealth/restforce/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/OurHealth/restforce/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/OurHealth/restforce/git/refs{/sha}","trees_url":"https://api.github.com/repos/OurHealth/restforce/git/trees{/sha}","statuses_url":"https://api.github.com/repos/OurHealth/restforce/statuses/{sha}","languages_url":"https://api.github.com/repos/OurHealth/restforce/languages","stargazers_url":"https://api.github.com/repos/OurHealth/restforce/stargazers","contributors_url":"https://api.github.com/repos/OurHealth/restforce/contributors","subscribers_url":"https://api.github.com/repos/OurHealth/restforce/subscribers","subscription_url":"https://api.github.com/repos/OurHealth/restforce/subscription","commits_url":"https://api.github.com/repos/OurHealth/restforce/commits{/sha}","git_commits_url":"https://api.github.com/repos/OurHealth/restforce/git/commits{/sha}","comments_url":"https://api.github.com/repos/OurHealth/restforce/comments{/number}","issue_comment_url":"https://api.github.com/repos/OurHealth/restforce/issues/comments{/number}","contents_url":"https://api.github.com/repos/OurHealth/restforce/contents/{+path}","compare_url":"https://api.github.com/repos/OurHealth/restforce/compare/{base}...{head}","merges_url":"https://api.github.com/repos/OurHealth/restforce/merges","archive_url":"https://api.github.com/repos/OurHealth/restforce/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/OurHealth/restforce/downloads","issues_url":"https://api.github.com/repos/OurHealth/restforce/issues{/number}","pulls_url":"https://api.github.com/repos/OurHealth/restforce/pulls{/number}","milestones_url":"https://api.github.com/repos/OurHealth/restforce/milestones{/number}","notifications_url":"https://api.github.com/repos/OurHealth/restforce/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/OurHealth/restforce/labels{/name}","releases_url":"https://api.github.com/repos/OurHealth/restforce/releases{/id}","deployments_url":"https://api.github.com/repos/OurHealth/restforce/deployments","created_at":"2016-10-21T03:44:54Z","updated_at":"2016-10-21T03:44:56Z","pushed_at":"2017-03-04T15:52:54Z","git_url":"git://github.com/OurHealth/restforce.git","ssh_url":"git@github.com:OurHealth/restforce.git","clone_url":"https://github.com/OurHealth/restforce.git","svn_url":"https://github.com/OurHealth/restforce","homepage":"","size":784,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":0,"forks":2,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"ejholmes:master","ref":"master","sha":"61fc4252633009d084dc9711933f3a2e7c5ee37b","user":{"login":"ejholmes","id":95166,"avatar_url":"https://avatars3.githubusercontent.com/u/95166?v=3","gravatar_id":"","url":"https://api.github.com/users/ejholmes","html_url":"https://github.com/ejholmes","followers_url":"https://api.github.com/users/ejholmes/followers","following_url":"https://api.github.com/users/ejholmes/following{/other_user}","gists_url":"https://api.github.com/users/ejholmes/gists{/gist_id}","starred_url":"https://api.github.com/users/ejholmes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ejholmes/subscriptions","organizations_url":"https://api.github.com/users/ejholmes/orgs","repos_url":"https://api.github.com/users/ejholmes/repos","events_url":"https://api.github.com/users/ejholmes/events{/privacy}","received_events_url":"https://api.github.com/users/ejholmes/received_events","type":"User","site_admin":false},"repo":{"id":5575055,"name":"restforce","full_name":"ejholmes/restforce","owner":{"login":"ejholmes","id":95166,"avatar_url":"https://avatars3.githubusercontent.com/u/95166?v=3","gravatar_id":"","url":"https://api.github.com/users/ejholmes","html_url":"https://github.com/ejholmes","followers_url":"https://api.github.com/users/ejholmes/followers","following_url":"https://api.github.com/users/ejholmes/following{/other_user}","gists_url":"https://api.github.com/users/ejholmes/gists{/gist_id}","starred_url":"https://api.github.com/users/ejholmes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ejholmes/subscriptions","organizations_url":"https://api.github.com/users/ejholmes/orgs","repos_url":"https://api.github.com/users/ejholmes/repos","events_url":"https://api.github.com/users/ejholmes/events{/privacy}","received_events_url":"https://api.github.com/users/ejholmes/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ejholmes/restforce","description":"A ruby client for the Salesforce REST api.","fork":false,"url":"https://api.github.com/repos/ejholmes/restforce","forks_url":"https://api.github.com/repos/ejholmes/restforce/forks","keys_url":"https://api.github.com/repos/ejholmes/restforce/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ejholmes/restforce/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ejholmes/restforce/teams","hooks_url":"https://api.github.com/repos/ejholmes/restforce/hooks","issue_events_url":"https://api.github.com/repos/ejholmes/restforce/issues/events{/number}","events_url":"https://api.github.com/repos/ejholmes/restforce/events","assignees_url":"https://api.github.com/repos/ejholmes/restforce/assignees{/user}","branches_url":"https://api.github.com/repos/ejholmes/restforce/branches{/branch}","tags_url":"https://api.github.com/repos/ejholmes/restforce/tags","blobs_url":"https://api.github.com/repos/ejholmes/restforce/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ejholmes/restforce/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ejholmes/restforce/git/refs{/sha}","trees_url":"https://api.github.com/repos/ejholmes/restforce/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ejholmes/restforce/statuses/{sha}","languages_url":"https://api.github.com/repos/ejholmes/restforce/languages","stargazers_url":"https://api.github.com/repos/ejholmes/restforce/stargazers","contributors_url":"https://api.github.com/repos/ejholmes/restforce/contributors","subscribers_url":"https://api.github.com/repos/ejholmes/restforce/subscribers","subscription_url":"https://api.github.com/repos/ejholmes/restforce/subscription","commits_url":"https://api.github.com/repos/ejholmes/restforce/commits{/sha}","git_commits_url":"https://api.github.com/repos/ejholmes/restforce/git/commits{/sha}","comments_url":"https://api.github.com/repos/ejholmes/restforce/comments{/number}","issue_comment_url":"https://api.github.com/repos/ejholmes/restforce/issues/comments{/number}","contents_url":"https://api.github.com/repos/ejholmes/restforce/contents/{+path}","compare_url":"https://api.github.com/repos/ejholmes/restforce/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ejholmes/restforce/merges","archive_url":"https://api.github.com/repos/ejholmes/restforce/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ejholmes/restforce/downloads","issues_url":"https://api.github.com/repos/ejholmes/restforce/issues{/number}","pulls_url":"https://api.github.com/repos/ejholmes/restforce/pulls{/number}","milestones_url":"https://api.github.com/repos/ejholmes/restforce/milestones{/number}","notifications_url":"https://api.github.com/repos/ejholmes/restforce/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ejholmes/restforce/labels{/name}","releases_url":"https://api.github.com/repos/ejholmes/restforce/releases{/id}","deployments_url":"https://api.github.com/repos/ejholmes/restforce/deployments","created_at":"2012-08-27T18:19:05Z","updated_at":"2017-02-28T22:38:56Z","pushed_at":"2017-03-04T15:52:55Z","git_url":"git://github.com/ejholmes/restforce.git","ssh_url":"git@github.com:ejholmes/restforce.git","clone_url":"https://github.com/ejholmes/restforce.git","svn_url":"https://github.com/ejholmes/restforce","homepage":"","size":792,"stargazers_count":424,"watchers_count":424,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":193,"mirror_url":null,"open_issues_count":58,"forks":193,"open_issues":58,"watchers":424,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ejholmes/restforce/pulls/265"},"html":{"href":"https://github.com/ejholmes/restforce/pull/265"},"issue":{"href":"https://api.github.com/repos/ejholmes/restforce/issues/265"},"comments":{"href":"https://api.github.com/repos/ejholmes/restforce/issues/265/comments"},"review_comments":{"href":"https://api.github.com/repos/ejholmes/restforce/pulls/265/comments"},"review_comment":{"href":"https://api.github.com/repos/ejholmes/restforce/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ejholmes/restforce/pulls/265/commits"},"statuses":{"href":"https://api.github.com/repos/ejholmes/restforce/statuses/808aa3cada2f181711e72b16fffa381a592e533b"}}}}
{"actor":{"display_login":"drteeth"}}