streamforce 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -8
- data/README.md +69 -1
- data/lib/streamforce/client.rb +55 -55
- data/lib/streamforce/extension/logging.rb +88 -9
- data/lib/streamforce/extension/replay.rb +22 -0
- data/lib/streamforce/message.rb +49 -0
- data/lib/streamforce/version.rb +1 -1
- data/lib/streamforce.rb +0 -1
- metadata +54 -12
- data/lib/streamforce/extension/subscription_tracking.rb +0 -58
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d03ff265d884babb70b8db70637c9814a5819834ed83109b2b26537cb5c992b
|
4
|
+
data.tar.gz: b7b87b62347f92e9a27139536655ee95339e8a3b22bf2c1980db1eca00fa66e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4dc737d70d28c061ee3c89379b97274983c14008184acbe7c99139b59c3ed79b3ef79893ba3d486d3f142172d30461260237d171bdb7a230c6f92471769a605
|
7
|
+
data.tar.gz: 6a3bbd281947060861c34e5cceab6814cfad52a80e964c80ef4da4956a3c40287e1315ef05419c69b31e60ac2bd20b394336e85a087c8fd41e22691543f6f7d7
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,76 @@
|
|
1
1
|
# Streamforce
|
2
2
|
|
3
|
+
In most cases, processing events received from the Salesforce Streaming API can be
|
4
|
+
broken down into three very specific steps:
|
5
|
+
|
6
|
+
1. Connecting to the Salesforce API and listening for messaging
|
7
|
+
2. Ingesting received messages into some sort of internal event bus (e.g. RabbitMQ,
|
8
|
+
Kafka, Redis, etc)
|
9
|
+
3. Processing the stored messages
|
10
|
+
|
11
|
+
Streamforce aims to handle #1 and simplify the work done for #2.
|
12
|
+
|
13
|
+
[Restforce](https://github.com/restforce/restforce) provides a simple API to connect
|
14
|
+
to the Streaming API and consume messages:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
# Restforce uses faye as the underlying implementation for CometD.
|
18
|
+
require 'faye'
|
19
|
+
|
20
|
+
# Initialize a client with your username/password/oauth token/etc.
|
21
|
+
client = Restforce.new(username: 'foo',
|
22
|
+
password: 'bar',
|
23
|
+
security_token: 'security token',
|
24
|
+
client_id: 'client_id',
|
25
|
+
client_secret: 'client_secret')
|
26
|
+
|
27
|
+
EM.run do
|
28
|
+
# Subscribe to the PushTopic.
|
29
|
+
client.subscription '/topic/AllAccounts' do |message|
|
30
|
+
puts message.inspect
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
However, the above code is usable in a production environment because:
|
36
|
+
|
37
|
+
* The interactions with the Streaming API need to be logged using the correct severity
|
38
|
+
(e.g. handshakes should use `Logger::DEBUG` while subscription errors should use
|
39
|
+
`Logger::ERROR` for better visibility)
|
40
|
+
* Replay IDs need to be stored using a persistent storage like Redis and not in-memory
|
41
|
+
|
42
|
+
Streamforce comes with all the batteries included.
|
43
|
+
|
44
|
+
## Usage
|
45
|
+
|
46
|
+
A very simple client, which automatically connects based on the following environment
|
47
|
+
variables:
|
48
|
+
|
49
|
+
* `SALESFORCE_USERNAME`
|
50
|
+
* `SALESFORCE_PASSWORD`
|
51
|
+
* `SALESFORCE_SECURITY_TOKEN`
|
52
|
+
* `SALESFORCE_CLIENT_ID`
|
53
|
+
* `SALESFORCE_CLIENT_SECRET`
|
54
|
+
* `REDIS_URL`
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
require "streamforce"
|
58
|
+
|
59
|
+
client = Streamforce::Client.new
|
60
|
+
|
61
|
+
subscriptions = %w[
|
62
|
+
/topic/account-monitor
|
63
|
+
/event/AccountUpdated__e
|
64
|
+
]
|
65
|
+
|
66
|
+
client.subscribe(subscriptions) do |subscription, message|
|
67
|
+
# Your code
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
3
71
|
## Contributing
|
4
72
|
|
5
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/andreimaxim/streamforce
|
73
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/andreimaxim/streamforce>.
|
6
74
|
|
7
75
|
## License
|
8
76
|
|
data/lib/streamforce/client.rb
CHANGED
@@ -1,79 +1,79 @@
|
|
1
1
|
class Streamforce::Client
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
def password
|
20
|
-
ENV["SALESFORCE_PASSWORD"]
|
21
|
-
end
|
22
|
-
|
23
|
-
def security_token
|
24
|
-
ENV["SALESFORCE_SECURITY_TOKEN"]
|
25
|
-
end
|
2
|
+
attr_reader :host, :username, :password, :client_id, :client_secret, :security_token,
|
3
|
+
:api_version
|
4
|
+
|
5
|
+
def initialize(opts = {})
|
6
|
+
@host = opts.fetch(:host, ENV["SALESFORCE_HOST"])
|
7
|
+
@username = opts.fetch(:username, ENV["SALESFORCE_USERNAME"])
|
8
|
+
@password = opts.fetch(:password, ENV["SALESFORCE_PASSWORD"])
|
9
|
+
@client_id = opts.fetch(:client_id, ENV["SALESFORCE_CLIENT_ID"])
|
10
|
+
@client_secret = opts.fetch(:client_secret, ENV["SALESFORCE_CLIENT_SECRET"])
|
11
|
+
@security_token = opts.fetch(:security_token, ENV["SALESFORCE_SECURITY_TOKEN"])
|
12
|
+
@api_version = opts.fetch(:api_version, ENV["SALESFORCE_API_VERSION"])
|
13
|
+
|
14
|
+
@logger = opts.fetch(:logger, Logger.new($stdout))
|
15
|
+
@logger.level = ENV.fetch("STREAMFORCE_LOG_LEVEL", Logger::INFO)
|
16
|
+
end
|
26
17
|
|
27
|
-
|
28
|
-
|
29
|
-
|
18
|
+
def subscribe(channels = [], &blk)
|
19
|
+
EM.run { subscribe_to_channels(faye, Array(channels), &blk) }
|
20
|
+
end
|
30
21
|
|
31
|
-
|
32
|
-
URI.parse("https://#{host}/services/oauth2/token")
|
33
|
-
end
|
22
|
+
private
|
34
23
|
|
35
|
-
|
36
|
-
|
37
|
-
grant_type: "password",
|
38
|
-
client_id: client_id,
|
39
|
-
client_secret: client_secret,
|
40
|
-
username: username,
|
41
|
-
password: "#{password}#{security_token}"
|
42
|
-
}
|
43
|
-
end
|
24
|
+
def instance_url
|
25
|
+
authentication["instance_url"]
|
44
26
|
end
|
45
27
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
28
|
+
def access_token
|
29
|
+
authentication["access_token"]
|
30
|
+
end
|
49
31
|
|
50
|
-
|
51
|
-
|
32
|
+
def authentication_url
|
33
|
+
URI.parse("https://#{host}/services/oauth2/token")
|
52
34
|
end
|
53
35
|
|
54
|
-
def
|
55
|
-
|
36
|
+
def authentication_params
|
37
|
+
{
|
38
|
+
grant_type: "password",
|
39
|
+
username: username,
|
40
|
+
password: "#{password}#{security_token}",
|
41
|
+
client_id: client_id,
|
42
|
+
client_secret: client_secret
|
43
|
+
}
|
44
|
+
end
|
56
45
|
|
57
|
-
|
46
|
+
def authentication
|
47
|
+
@authentication ||= fetch_authentication_credentials
|
48
|
+
end
|
58
49
|
|
59
|
-
|
50
|
+
def fetch_authentication_credentials
|
51
|
+
response = Net::HTTP.post_form(authentication_url, authentication_params)
|
52
|
+
JSON.parse(response.body)
|
60
53
|
end
|
61
54
|
|
62
55
|
def faye
|
63
|
-
@faye ||= Faye::Client.new(
|
64
|
-
client.set_header "Authorization", "OAuth #{
|
56
|
+
@faye ||= Faye::Client.new("#{instance_url}/cometd/#{api_version}").tap do |client|
|
57
|
+
client.set_header "Authorization", "OAuth #{access_token}"
|
65
58
|
|
66
59
|
client.add_extension Streamforce::Extension::Replay.new
|
67
|
-
client.add_extension Streamforce::Extension::
|
68
|
-
client.add_extension Streamforce::Extension::Logging.new
|
60
|
+
client.add_extension Streamforce::Extension::Logging.new(@logger)
|
69
61
|
end
|
70
62
|
end
|
71
63
|
|
72
64
|
def subscribe_to_channels(client, channels, &blk)
|
73
65
|
return if channels.empty?
|
74
66
|
|
75
|
-
|
76
|
-
|
77
|
-
|
67
|
+
# Subscribe to a single channel, otherwise Salesforce will return a 403 Unknown Client error
|
68
|
+
subscription = client.subscribe(channels.shift)
|
69
|
+
|
70
|
+
# Allow clients to receive [ channel, message ] block params
|
71
|
+
subscription.with_channel(&blk)
|
72
|
+
|
73
|
+
# Continue subscribing to the rest of the channels, regadless of the current subscription
|
74
|
+
# status
|
75
|
+
subscription
|
76
|
+
.callback { subscribe_to_channels(client, channels, &blk) }
|
77
|
+
.errback { subscribe_to_channels(client, channels, &blk) }
|
78
78
|
end
|
79
79
|
end
|
@@ -1,18 +1,97 @@
|
|
1
1
|
class Streamforce::Extension::Logging
|
2
|
-
def initialize(
|
3
|
-
@logger =
|
4
|
-
@logger.level = log_level
|
2
|
+
def initialize(logger)
|
3
|
+
@logger = logger
|
5
4
|
end
|
6
5
|
|
7
|
-
def incoming(
|
8
|
-
|
6
|
+
def incoming(payload, callback)
|
7
|
+
message = Streamforce::Message.new(payload)
|
8
|
+
log_incoming_message(message)
|
9
9
|
|
10
|
-
callback.call(
|
10
|
+
callback.call(payload)
|
11
11
|
end
|
12
12
|
|
13
|
-
def outgoing(
|
14
|
-
|
13
|
+
def outgoing(payload, callback)
|
14
|
+
message = Streamforce::Message.new(payload)
|
15
|
+
log_outgoing_message(message)
|
15
16
|
|
16
|
-
callback.call(
|
17
|
+
callback.call(payload)
|
18
|
+
end
|
19
|
+
|
20
|
+
def log_incoming_message(message)
|
21
|
+
if message.channel_type == "meta"
|
22
|
+
public_send("log_incoming_#{message.channel_name}", message)
|
23
|
+
else
|
24
|
+
@logger.debug "[#{message.channel_name}] #{message.data}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def log_outgoing_message(message)
|
29
|
+
if message.channel_type == "meta"
|
30
|
+
public_send("log_outgoing_#{message.channel_name}", message)
|
31
|
+
else
|
32
|
+
@logger.debug "[#{message.channel_name}] #{message.data}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def log_outgoing_handshake(message)
|
37
|
+
@logger.debug "[Client] Handshake requested..."
|
38
|
+
end
|
39
|
+
|
40
|
+
def log_incoming_handshake(message)
|
41
|
+
if message.success?
|
42
|
+
@logger.debug "[Server] Handshake accepted, assigning client_id=#{message.client_id}"
|
43
|
+
else
|
44
|
+
@logger.error "[Server] Connection was refused: #{message.error_message}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def log_outgoing_connect(message)
|
49
|
+
debug message, "Sending connection request"
|
50
|
+
end
|
51
|
+
|
52
|
+
def log_incoming_connect(message)
|
53
|
+
if message.success?
|
54
|
+
debug message, "Connection successful!"
|
55
|
+
else
|
56
|
+
error message, "Connection failed: #{message.error_message}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def log_outgoing_subscribe(message)
|
61
|
+
replay_id = message.replay_id
|
62
|
+
|
63
|
+
replay_info = if replay_id == -1
|
64
|
+
"for all new messages"
|
65
|
+
elsif replay_id == -2
|
66
|
+
"and requesting all stored messages"
|
67
|
+
else
|
68
|
+
"and requesting all messages newer than ##{replay_id}"
|
69
|
+
end
|
70
|
+
|
71
|
+
info message, "Subscribing to #{message.subscription} #{replay_info}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def log_incoming_subscribe(message)
|
75
|
+
if message.success?
|
76
|
+
info message, "Successfully subscribed to #{message.subscription}"
|
77
|
+
else
|
78
|
+
error message, "Subscription for #{message.subscription} failed: #{message.error_message}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def debug(message, text)
|
83
|
+
@logger.debug "[#{message.client_id}##{message.id}] #{text}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def info(message, text)
|
87
|
+
@logger.info "[#{message.client_id}##{message.id}] #{text}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def warn(message, text)
|
91
|
+
@logger.warn "[#{message.client_id}##{message.id}] #{text}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def error(message, text)
|
95
|
+
@logger.error "[#{message.client_id}##{message.id}] #{text}"
|
17
96
|
end
|
18
97
|
end
|
@@ -1,14 +1,36 @@
|
|
1
|
+
require "redis"
|
2
|
+
|
1
3
|
class Streamforce::Extension::Replay
|
2
4
|
def initialize(log_level = Logger::INFO)
|
3
5
|
@logger = Logger.new($stdout)
|
4
6
|
@logger.level = log_level
|
7
|
+
@redis = Redis.new
|
5
8
|
end
|
6
9
|
|
7
10
|
def incoming(message, callback)
|
11
|
+
replay_id = message.dig "data", "event", "replayId"
|
12
|
+
channel = message["channel"]
|
13
|
+
|
14
|
+
store(channel, replay_id)
|
8
15
|
callback.call(message)
|
9
16
|
end
|
10
17
|
|
11
18
|
def outgoing(message, callback)
|
19
|
+
return callback.call(message) unless message["channel"] == "/meta/subscribe"
|
20
|
+
|
21
|
+
channel = message["subscription"]
|
22
|
+
message["ext"] = { "replay" => { channel => replay_id(channel).to_i } }
|
23
|
+
|
12
24
|
callback.call(message)
|
13
25
|
end
|
26
|
+
|
27
|
+
def store(channel, replay_id)
|
28
|
+
return if channel.nil? || replay_id.nil?
|
29
|
+
|
30
|
+
@redis.set channel, replay_id, ex: 86400
|
31
|
+
end
|
32
|
+
|
33
|
+
def replay_id(channel)
|
34
|
+
@redis.get(channel) || -1
|
35
|
+
end
|
14
36
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Streamforce::Message
|
2
|
+
def initialize(payload)
|
3
|
+
@payload = payload
|
4
|
+
end
|
5
|
+
|
6
|
+
def success?
|
7
|
+
@payload["successful"]
|
8
|
+
end
|
9
|
+
|
10
|
+
def id
|
11
|
+
@payload["id"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def replay_id
|
15
|
+
@payload.dig "ext", "replay", subscription
|
16
|
+
end
|
17
|
+
|
18
|
+
def channel
|
19
|
+
@payload["channel"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def client_id
|
23
|
+
@payload["clientId"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def channel_type
|
27
|
+
channel.split("/")[1]
|
28
|
+
end
|
29
|
+
|
30
|
+
def channel_name
|
31
|
+
channel.split("/")[2]
|
32
|
+
end
|
33
|
+
|
34
|
+
def data
|
35
|
+
@payload["data"]
|
36
|
+
end
|
37
|
+
|
38
|
+
def subscription
|
39
|
+
@payload["subscription"]
|
40
|
+
end
|
41
|
+
|
42
|
+
def subscription?
|
43
|
+
channel == "/meta/subscribe"
|
44
|
+
end
|
45
|
+
|
46
|
+
def error_message
|
47
|
+
@payload["error"]
|
48
|
+
end
|
49
|
+
end
|
data/lib/streamforce/version.rb
CHANGED
data/lib/streamforce.rb
CHANGED
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: streamforce
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrei Maxim
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-06-
|
11
|
+
date: 2024-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: zeitwerk
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: faye
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: relaxed_cookiejar
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: redis
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -66,7 +66,49 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '13.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '13.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-rails-omakase
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
70
112
|
email:
|
71
113
|
- andrei@andreimaxim.ro
|
72
114
|
executables: []
|
@@ -82,7 +124,7 @@ files:
|
|
82
124
|
- lib/streamforce/client.rb
|
83
125
|
- lib/streamforce/extension/logging.rb
|
84
126
|
- lib/streamforce/extension/replay.rb
|
85
|
-
- lib/streamforce/
|
127
|
+
- lib/streamforce/message.rb
|
86
128
|
- lib/streamforce/version.rb
|
87
129
|
homepage: https://github.com/andreimaxim/streamforce
|
88
130
|
licenses:
|
@@ -91,7 +133,7 @@ metadata:
|
|
91
133
|
homepage_uri: https://github.com/andreimaxim/streamforce
|
92
134
|
source_code_uri: https://github.com/andreimaxim/streamforce
|
93
135
|
changelog_uri: https://github.com/andreimaxim/streamforce/blob/main/CHANGELOG.md
|
94
|
-
post_install_message:
|
136
|
+
post_install_message:
|
95
137
|
rdoc_options: []
|
96
138
|
require_paths:
|
97
139
|
- lib
|
@@ -99,7 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
141
|
requirements:
|
100
142
|
- - ">="
|
101
143
|
- !ruby/object:Gem::Version
|
102
|
-
version: 3.
|
144
|
+
version: 3.0.0
|
103
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
146
|
requirements:
|
105
147
|
- - ">="
|
@@ -107,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
149
|
version: '0'
|
108
150
|
requirements: []
|
109
151
|
rubygems_version: 3.5.9
|
110
|
-
signing_key:
|
152
|
+
signing_key:
|
111
153
|
specification_version: 4
|
112
154
|
summary: Small wrapper over the Salesforce Streaming API
|
113
155
|
test_files: []
|
@@ -1,58 +0,0 @@
|
|
1
|
-
class Streamforce::Extension::SubscriptionTracking
|
2
|
-
def initialize(log_level = Logger::DEBUG)
|
3
|
-
@logger = Logger.new($stdout)
|
4
|
-
@logger.level = log_level
|
5
|
-
|
6
|
-
@subscriptions = []
|
7
|
-
end
|
8
|
-
|
9
|
-
def incoming(message, callback)
|
10
|
-
if subscription?(message)
|
11
|
-
log_subscription_status(message)
|
12
|
-
else
|
13
|
-
log_subscription_payload(message)
|
14
|
-
end
|
15
|
-
|
16
|
-
callback.call(message)
|
17
|
-
end
|
18
|
-
|
19
|
-
def outgoing(message, callback)
|
20
|
-
@logger.debug "Requested subscription for #{message["subscription"]}" if subscription?(message)
|
21
|
-
|
22
|
-
callback.call(message)
|
23
|
-
end
|
24
|
-
|
25
|
-
def log_subscription_status(message)
|
26
|
-
subscription = message["subscription"]
|
27
|
-
|
28
|
-
if message["successful"]
|
29
|
-
@subscriptions << subscription
|
30
|
-
|
31
|
-
@logger.info "Subscription for #{subscription} was successful"
|
32
|
-
else
|
33
|
-
@logger.warn "Subscription for #{subscription} failed: #{message["error"]}"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def log_subscription_payload(message)
|
38
|
-
channel = message["channel"]
|
39
|
-
payload = message["data"].to_json
|
40
|
-
|
41
|
-
type = case channel.split("/")[1]
|
42
|
-
when "topic"
|
43
|
-
"PushTopic"
|
44
|
-
when "event"
|
45
|
-
"PlatformEvent"
|
46
|
-
else
|
47
|
-
"Unknown"
|
48
|
-
end
|
49
|
-
|
50
|
-
name = channel.split("/")[2]
|
51
|
-
|
52
|
-
@logger.info "[#{type}][#{name}]: #{payload}" if @subscriptions.include?(channel)
|
53
|
-
end
|
54
|
-
|
55
|
-
def subscription?(message)
|
56
|
-
message["channel"] == "/meta/subscribe"
|
57
|
-
end
|
58
|
-
end
|