ribbon-intercom 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ribbon/intercom/client/mock_sdk.rb +13 -0
- data/lib/ribbon/intercom/client/sdk/adapters/adapter/response.rb +38 -0
- data/lib/ribbon/intercom/client/sdk/adapters/adapter.rb +51 -0
- data/lib/ribbon/intercom/client/sdk/adapters/http_adapter/connection.rb +34 -0
- data/lib/ribbon/intercom/client/sdk/adapters/http_adapter.rb +26 -0
- data/lib/ribbon/intercom/client/sdk/adapters/local_adapter.rb +54 -0
- data/lib/ribbon/intercom/client/sdk/adapters/mock_adapter.rb +40 -0
- data/lib/ribbon/intercom/client/sdk/adapters.rb +10 -0
- data/lib/ribbon/intercom/client/sdk.rb +71 -26
- data/lib/ribbon/intercom/client.rb +35 -7
- data/lib/ribbon/intercom/errors.rb +34 -6
- data/lib/ribbon/intercom/package.rb +64 -0
- data/lib/ribbon/intercom/packageable/mixin.rb +35 -0
- data/lib/ribbon/intercom/packageable.rb +6 -0
- data/lib/ribbon/intercom/service/channel/stores/mock_store.rb +40 -0
- data/lib/ribbon/intercom/service/channel/stores/redis_store.rb +186 -0
- data/lib/ribbon/intercom/service/{channel_stores/base.rb → channel/stores/store.rb} +13 -5
- data/lib/ribbon/intercom/service/channel/stores.rb +9 -0
- data/lib/ribbon/intercom/service/channel.rb +106 -4
- data/lib/ribbon/intercom/service.rb +156 -95
- data/lib/ribbon/intercom/utils/mixins/mock_safe.rb +26 -0
- data/lib/ribbon/intercom/utils/mixins.rb +5 -0
- data/lib/ribbon/intercom/utils/signer.rb +71 -0
- data/lib/ribbon/intercom/utils.rb +40 -13
- data/lib/ribbon/intercom/version.rb +1 -1
- data/lib/ribbon/intercom.rb +10 -7
- data/lib/tasks/intercom.rake +3 -3
- metadata +20 -20
- data/lib/ribbon/intercom/client/sdk/connection.rb +0 -35
- data/lib/ribbon/intercom/client/sdk/response.rb +0 -59
- data/lib/ribbon/intercom/service/channel_stores/redis_store.rb +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 977c89158ba0646225543c4b65b86fc9db5663b4
|
4
|
+
data.tar.gz: 9482ff868c0d2e7974cbf08e329c8e18eb85b21a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ac6cdc49ae1e738c21ad1cdc91a1d02340c7d04d35fe50a3eab1d9ca08e767366b3725b828b21f2e29b132a5bec70668683b337e80096e5b02e1c8f4063650f
|
7
|
+
data.tar.gz: ed7da55ea2c9dca75e712cf83f691977ed3aec4bb01f6ed0f5dcdf6999320d59b6dd091b954107f19cdaa7bc20985882c1293177e74f5f2e281720ca0080e5f0
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Ribbon::Intercom
|
2
|
+
class Client
|
3
|
+
class MockSDK < SDK
|
4
|
+
def initialize(service)
|
5
|
+
super(Adapters::MockAdapter.new(service))
|
6
|
+
end
|
7
|
+
|
8
|
+
def with_permissions(*perms, &block)
|
9
|
+
adapter.with_permissions(*perms, &block)
|
10
|
+
end
|
11
|
+
end # MockSDK
|
12
|
+
end # Client
|
13
|
+
end # Ribbon::Intercom
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Ribbon::Intercom
|
2
|
+
class Client::SDK::Adapters::Adapter
|
3
|
+
class Response < Rack::Response
|
4
|
+
attr_reader :method_name
|
5
|
+
|
6
|
+
def initialize(body, status, headers, method_name)
|
7
|
+
super(body, status, headers)
|
8
|
+
@method_name = method_name.to_sym
|
9
|
+
end
|
10
|
+
|
11
|
+
def missing_permissions
|
12
|
+
@__missing_permissions ||= _intercom_permissions_missing
|
13
|
+
end
|
14
|
+
|
15
|
+
def body
|
16
|
+
super.join
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Decode the response body received from the service.
|
21
|
+
def retval
|
22
|
+
@__retval ||= Marshal.load(Base64.strict_decode64(body)) unless body.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def _intercom_permissions_missing
|
28
|
+
key = _intercom_permissions_missing_header
|
29
|
+
headers[key] && headers[key].split(',').to_set
|
30
|
+
end
|
31
|
+
|
32
|
+
def _intercom_permissions_missing_header
|
33
|
+
missing_keys = ['X-Intercom-Permissions-Missing', :x_intercom_permissions_missing]
|
34
|
+
missing_keys.detect { |key| headers.key?(key) }
|
35
|
+
end
|
36
|
+
end # Response
|
37
|
+
end # Client::SDK::Adapters::Adapter
|
38
|
+
end # Ribbon::Intercom
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Ribbon::Intercom
|
2
|
+
module Client::SDK::Adapters
|
3
|
+
class Adapter
|
4
|
+
autoload(:Response, 'ribbon/intercom/client/sdk/adapters/adapter/response')
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
connect(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
##
|
11
|
+
# Call the method on the service.
|
12
|
+
def call(method_name, *args)
|
13
|
+
call!(method_name, _encode_args(*args)).tap { |response|
|
14
|
+
raise TypeError, "call! should return an Adapter::Response" unless response.is_a?(Response)
|
15
|
+
raise "response should have correct method name" unless response.method_name == method_name.to_sym
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def headers(h={})
|
20
|
+
(@__headers ||= {}).merge!(h)
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# Connect to a service. The specific arguments depend on the Adapter
|
25
|
+
# subclass.
|
26
|
+
def connect(*args)
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Returns whether or not the Adapter is connected to a service.
|
32
|
+
def connected?
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Actually call the method on the service. Should return an Adapter::Response object.
|
38
|
+
def call!(method_name, *args)
|
39
|
+
raise NotImplementedError
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
##
|
45
|
+
# Encode the arguments for transmission to the service.
|
46
|
+
def _encode_args(*args)
|
47
|
+
Base64.strict_encode64(Marshal.dump(Utils.sanitize(args)))
|
48
|
+
end
|
49
|
+
end # Adapter
|
50
|
+
end # Client::SDK::Adapters
|
51
|
+
end # Ribbon::Intercom
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'openssl'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Ribbon::Intercom
|
6
|
+
module Client::SDK::Adapters
|
7
|
+
class HttpAdapter::Connection
|
8
|
+
attr_reader :url
|
9
|
+
|
10
|
+
def initialize(url, token, secret)
|
11
|
+
@url = url.is_a?(String) ? URI(url) : url
|
12
|
+
|
13
|
+
@_client = Net::HTTP.new(url.hostname, url.port)
|
14
|
+
@_client.use_ssl = url.is_a?(URI::HTTPS)
|
15
|
+
@_client.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
16
|
+
@_client.cert_store = OpenSSL::X509::Store.new.tap { |s| s.set_default_paths }
|
17
|
+
|
18
|
+
@_headers = {
|
19
|
+
'Authorization' => "Basic #{Base64.strict_encode64("#{token}:#{secret}")}"
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def put(params={})
|
24
|
+
@_client.put(url.request_uri, params[:body], _prepare_headers(params[:headers]).merge(@_headers))
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def _prepare_headers(headers)
|
30
|
+
Hash[(headers || {}).map { |k, v| [k.to_s, v.to_s] }]
|
31
|
+
end
|
32
|
+
end # HttpAdapter::Connection
|
33
|
+
end # Client::SDK::Adapters
|
34
|
+
end # Ribbon::Intercom
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ribbon::Intercom
|
2
|
+
module Client::SDK::Adapters
|
3
|
+
class HttpAdapter < Adapter
|
4
|
+
autoload(:Connection, 'ribbon/intercom/client/sdk/adapters/http_adapter/connection')
|
5
|
+
|
6
|
+
attr_reader :connection
|
7
|
+
|
8
|
+
def connect(*args)
|
9
|
+
@connection = Connection.new(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def connected?
|
13
|
+
!!connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def call!(method_name, encoded_args)
|
17
|
+
response = connection.put(
|
18
|
+
headers: headers.merge("X-Intercom-Method" => method_name),
|
19
|
+
body: encoded_args
|
20
|
+
)
|
21
|
+
|
22
|
+
Adapter::Response.new(response.body, response.code.to_i, response, method_name)
|
23
|
+
end
|
24
|
+
end # HttpAdapter
|
25
|
+
end # Client::SDK::Adapters
|
26
|
+
end # Ribbon::Intercom
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Ribbon::Intercom
|
4
|
+
module Client::SDK::Adapters
|
5
|
+
class LocalAdapter < Adapter
|
6
|
+
attr_reader :service
|
7
|
+
attr_accessor :channel_token
|
8
|
+
attr_accessor :channel_secret
|
9
|
+
|
10
|
+
def connect(service, params={})
|
11
|
+
case service
|
12
|
+
when Service
|
13
|
+
@service = service
|
14
|
+
else
|
15
|
+
raise ArgumentError, "Expected a service, got: #{service.inspect}"
|
16
|
+
end
|
17
|
+
|
18
|
+
@channel_token = params[:channel_token]
|
19
|
+
@channel_secret = params[:channel_secret]
|
20
|
+
end
|
21
|
+
|
22
|
+
def connected?(*args)
|
23
|
+
!!service
|
24
|
+
end
|
25
|
+
|
26
|
+
def call!(method_name, encoded_args)
|
27
|
+
response = service.call(
|
28
|
+
_http_headers.merge(
|
29
|
+
'HTTP_AUTHORIZATION' => _http_auth,
|
30
|
+
'HTTP_X_INTERCOM_METHOD' => method_name.to_s,
|
31
|
+
'REQUEST_METHOD' => 'PUT',
|
32
|
+
'rack.input' => StringIO.new(encoded_args)
|
33
|
+
)
|
34
|
+
)
|
35
|
+
|
36
|
+
Response.new(response[2], response[0], response[1], method_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def _http_auth
|
42
|
+
"Basic #{Base64.strict_encode64("#{channel_token}:#{channel_secret}")}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def _http_headers
|
46
|
+
Hash[headers.map { |name, value| [_header_to_http(name), value] }]
|
47
|
+
end
|
48
|
+
|
49
|
+
def _header_to_http(name)
|
50
|
+
name.to_s.upcase.gsub('-', '_')
|
51
|
+
end
|
52
|
+
end # MockAdapter
|
53
|
+
end # Client::SDK::Adapters
|
54
|
+
end # Ribbon::Intercom
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Ribbon::Intercom
|
2
|
+
module Client::SDK::Adapters
|
3
|
+
class MockAdapter < LocalAdapter
|
4
|
+
attr_reader :store
|
5
|
+
|
6
|
+
def connect(service)
|
7
|
+
if service.is_a?(Class) && service < Service
|
8
|
+
service = service.new(store: Service::Channel::Stores::MockStore.new)
|
9
|
+
end
|
10
|
+
|
11
|
+
unless service.is_a?(Service)
|
12
|
+
raise ArgumentError, "Expected a service, got: #{service.inspect}"
|
13
|
+
end
|
14
|
+
|
15
|
+
unless service.store.is_a?(Service::Channel::Stores::MockStore)
|
16
|
+
raise ArgumentError, "Expected service to have a MockStore, got: #{service.store.inspect}"
|
17
|
+
end
|
18
|
+
|
19
|
+
super(service)
|
20
|
+
@store = service.store
|
21
|
+
end
|
22
|
+
|
23
|
+
def with_permissions(*perms, &block)
|
24
|
+
channel = store.open_channel(name: 'mock channel', may: perms)
|
25
|
+
secret = channel.rotate_secret!
|
26
|
+
with_channel(channel.token, secret, &block)
|
27
|
+
ensure
|
28
|
+
channel.close
|
29
|
+
end
|
30
|
+
|
31
|
+
def with_channel(token, secret)
|
32
|
+
token_prv, secret_prv = self.channel_token, self.channel_secret
|
33
|
+
self.channel_token, self.channel_secret = token, secret
|
34
|
+
yield
|
35
|
+
ensure
|
36
|
+
self.channel_token, self.channel_secret = token_prv, secret_prv
|
37
|
+
end
|
38
|
+
end # MockAdapter
|
39
|
+
end # Client::SDK::Adapters
|
40
|
+
end # Ribbon::Intercom
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Ribbon::Intercom
|
2
|
+
class Client::SDK
|
3
|
+
module Adapters
|
4
|
+
autoload(:Adapter, 'ribbon/intercom/client/sdk/adapters/adapter')
|
5
|
+
autoload(:HttpAdapter, 'ribbon/intercom/client/sdk/adapters/http_adapter')
|
6
|
+
autoload(:LocalAdapter, 'ribbon/intercom/client/sdk/adapters/local_adapter')
|
7
|
+
autoload(:MockAdapter, 'ribbon/intercom/client/sdk/adapters/mock_adapter')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -1,49 +1,94 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
1
3
|
module Ribbon::Intercom
|
2
4
|
class Client
|
3
5
|
class SDK
|
4
|
-
autoload(:
|
5
|
-
|
6
|
+
autoload(:Adapters, 'ribbon/intercom/client/sdk/adapters')
|
7
|
+
|
8
|
+
include Utils::Mixins::MockSafe
|
6
9
|
|
7
|
-
attr_reader :
|
10
|
+
attr_reader :adapter
|
8
11
|
|
9
12
|
def initialize(*args)
|
10
|
-
|
13
|
+
case args.first
|
14
|
+
when Adapters::Adapter
|
15
|
+
@adapter = args.first
|
16
|
+
else
|
17
|
+
@adapter = Adapters::HttpAdapter.new(*args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def headers(h={})
|
22
|
+
adapter.headers(h)
|
11
23
|
end
|
12
24
|
|
13
25
|
def connect(*args)
|
14
|
-
|
26
|
+
adapter.connect(*args)
|
15
27
|
end
|
16
28
|
|
17
29
|
def connected?
|
18
|
-
|
30
|
+
adapter.connected?
|
19
31
|
end
|
20
32
|
|
21
|
-
|
22
|
-
|
33
|
+
##
|
34
|
+
# Calls the method on the adapter returning the Adapter::Response.
|
35
|
+
#
|
36
|
+
# Intended to be called by Package.
|
37
|
+
def call(method_name, *args)
|
38
|
+
_process_response(adapter.call(method_name, *args))
|
23
39
|
end
|
24
40
|
|
25
41
|
private
|
26
42
|
|
27
|
-
|
28
|
-
|
43
|
+
##
|
44
|
+
# Simulates calling the remote method as if it were a local method.
|
45
|
+
#
|
46
|
+
# Intended to be called by end-users.
|
47
|
+
def method_missing(meth, *args, &block)
|
48
|
+
call(meth, *args).retval
|
49
|
+
end
|
29
50
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
51
|
+
##
|
52
|
+
# Process an Adapter::Response object returned by Adapter#call.
|
53
|
+
def _process_response(response)
|
54
|
+
_handle_response_error(response) unless response.successful?
|
55
|
+
|
56
|
+
_init_packages(response.retval)
|
57
|
+
response
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Raises an error depending on what went wrong.
|
62
|
+
def _handle_response_error(response)
|
63
|
+
raise 'called for successful response' if response.successful?
|
64
|
+
|
65
|
+
encoded_error = response.headers['X-Intercom-Error']
|
66
|
+
|
67
|
+
if encoded_error && !encoded_error.empty?
|
68
|
+
begin
|
69
|
+
decoded_error = Base64.strict_decode64(encoded_error)
|
70
|
+
error = Marshal.load(decoded_error)
|
71
|
+
rescue
|
72
|
+
error = Errors::ServerError.new('unknown server error')
|
44
73
|
end
|
45
74
|
end
|
75
|
+
|
76
|
+
raise error || Errors::ServerError.new('unexpected server error')
|
77
|
+
end # _handle_response
|
78
|
+
|
79
|
+
##
|
80
|
+
# Walks the object and initializes all packages (i.e., sets them up to be
|
81
|
+
# accessed by the end-user on the client).
|
82
|
+
def _init_packages(object)
|
83
|
+
Utils.walk(object) { |object|
|
84
|
+
if object.is_a?(Package)
|
85
|
+
object.sdk = dup
|
86
|
+
object.mock_safe! if mock_safe?
|
87
|
+
end
|
88
|
+
|
89
|
+
object
|
90
|
+
}
|
46
91
|
end
|
47
|
-
end #
|
48
|
-
end #
|
92
|
+
end # SDK
|
93
|
+
end # Client
|
49
94
|
end # Ribbon::Intercom
|
@@ -3,6 +3,7 @@ require 'ribbon/config'
|
|
3
3
|
module Ribbon::Intercom
|
4
4
|
class Client
|
5
5
|
autoload(:SDK, 'ribbon/intercom/client/sdk')
|
6
|
+
autoload(:MockSDK, 'ribbon/intercom/client/mock_sdk')
|
6
7
|
|
7
8
|
def config(&block)
|
8
9
|
(@__config ||= Ribbon::Config.new).tap { |config|
|
@@ -16,16 +17,43 @@ module Ribbon::Intercom
|
|
16
17
|
_load_sdk(handle)
|
17
18
|
end
|
18
19
|
|
19
|
-
|
20
|
-
|
20
|
+
##
|
21
|
+
# Return a mock safe version of this client.
|
22
|
+
def mock_safe
|
23
|
+
dup.tap { |client|
|
24
|
+
client.preload_sdks
|
21
25
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
# Make SDK instances mock safe
|
27
|
+
client.sdk_instances = Hash[
|
28
|
+
client.sdk_instances.map { |service, sdk| [service, sdk.mock_safe] }
|
29
|
+
]
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def preload_sdks
|
34
|
+
if config.service?
|
35
|
+
config.service.each { |name, *args|
|
36
|
+
_load_sdk(name)
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
attr_accessor :sdk_instances
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def _load_sdk(handle)
|
48
|
+
raise Errors::ServiceNotDefinedError unless config.service?
|
49
|
+
|
50
|
+
(self.sdk_instances ||= Hash.new { |hash, key|
|
51
|
+
# Get the last service if there are duplicates
|
52
|
+
if (service_def = config.service.select { |r| r.first == key }.last)
|
53
|
+
name, config = service_def
|
26
54
|
hash[key] = SDK.new(config[:url], config[:token], config[:secret])
|
27
55
|
else
|
28
|
-
raise Errors::
|
56
|
+
raise Errors::ServiceNotDefinedError
|
29
57
|
end
|
30
58
|
})[handle.to_sym]
|
31
59
|
end
|
@@ -2,14 +2,44 @@ module Ribbon::Intercom
|
|
2
2
|
module Errors
|
3
3
|
class Error < StandardError; end
|
4
4
|
|
5
|
+
#############
|
6
|
+
# Http Errors
|
7
|
+
class HttpError < Error; end
|
8
|
+
|
9
|
+
##
|
10
|
+
# Request Errors
|
11
|
+
|
12
|
+
# 400 Error
|
13
|
+
class RequestError < HttpError; end
|
14
|
+
class UnsafeArgumentError < RequestError; end
|
15
|
+
class InvalidSubjectSignatureError < RequestError; end
|
16
|
+
|
17
|
+
# 401 Error
|
18
|
+
class AuthenticationError < RequestError; end
|
19
|
+
|
20
|
+
# 403 Error
|
21
|
+
class ForbiddenError < RequestError; end
|
22
|
+
class InsufficientPermissionsError < ForbiddenError; end
|
23
|
+
|
24
|
+
# 404 Not Found
|
25
|
+
class NotFoundError < RequestError; end
|
26
|
+
class InvalidMethodError < NotFoundError; end
|
27
|
+
|
28
|
+
# 405 Method Not Allowed
|
29
|
+
class MethodNotAllowedError < RequestError; end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Server Errors
|
33
|
+
|
34
|
+
# 500 Error
|
35
|
+
class ServerError < HttpError; end
|
36
|
+
class UnsafeResponseError < ServerError; end
|
37
|
+
|
5
38
|
# General Errors
|
6
|
-
class MissingPermissionsError < Error; end
|
7
|
-
class ServerError < Error; end
|
8
39
|
class UnsafeValueError < Error; end
|
9
40
|
|
10
41
|
# Intercom Errors
|
11
|
-
class
|
12
|
-
class NoRemotesCreatedError < Error; end
|
42
|
+
class ServiceNotDefinedError < Error; end
|
13
43
|
|
14
44
|
# Service Errors
|
15
45
|
class NoPermissionsError < Error; end
|
@@ -22,8 +52,6 @@ module Ribbon::Intercom
|
|
22
52
|
|
23
53
|
# SDK Errors
|
24
54
|
class RequestFailureError < Error; end
|
25
|
-
class InvalidMethodError < Error; end
|
26
|
-
class AuthenticationError < Error; end
|
27
55
|
|
28
56
|
# Channel Errors
|
29
57
|
class ChannelNameMissingError < Error; end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Ribbon::Intercom
|
4
|
+
class Package
|
5
|
+
include Utils::Mixins::MockSafe
|
6
|
+
|
7
|
+
attr_reader :sdk
|
8
|
+
|
9
|
+
def initialize(subject_data=nil, data=nil)
|
10
|
+
self._subject_data = subject_data
|
11
|
+
@_data = data
|
12
|
+
end
|
13
|
+
|
14
|
+
def sdk=(sdk)
|
15
|
+
_set_sdk_headers(sdk)
|
16
|
+
@sdk = sdk
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def _subject_data=(data)
|
22
|
+
@_subject_data = data
|
23
|
+
_set_sdk_headers
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def method_missing(meth, *args, &block)
|
29
|
+
if @_data && @_data.key?(meth)
|
30
|
+
@_data[meth]
|
31
|
+
elsif sdk
|
32
|
+
_process_response(sdk.call(meth, *args))
|
33
|
+
else
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def marshal_dump
|
39
|
+
[@_subject_data, @_data]
|
40
|
+
end
|
41
|
+
|
42
|
+
def marshal_load(array)
|
43
|
+
self._subject_data = array[0]
|
44
|
+
@_data = array[1]
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def _set_sdk_headers(sdk=sdk)
|
49
|
+
sdk.headers('X-Intercom-Subject' => @_subject_data) if sdk
|
50
|
+
end
|
51
|
+
|
52
|
+
def _process_response(response)
|
53
|
+
self._subject_data = response.headers['X-Intercom-Subject']
|
54
|
+
@_data = _retrieve_data_from_response(response)
|
55
|
+
|
56
|
+
response.retval
|
57
|
+
end
|
58
|
+
|
59
|
+
def _retrieve_data_from_response(response)
|
60
|
+
encoded_data = response.headers['X-Intercom-Package-Data']
|
61
|
+
encoded_data && Marshal.load(Base64.strict_decode64(encoded_data))
|
62
|
+
end
|
63
|
+
end # Package
|
64
|
+
end # Ribbon::Intercom
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Ribbon::Intercom
|
4
|
+
class Packageable
|
5
|
+
module Mixin
|
6
|
+
class << self
|
7
|
+
def included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def package_with(*args)
|
14
|
+
args.map { |m| _package_with_methods << m.to_sym }
|
15
|
+
end
|
16
|
+
|
17
|
+
def _package_with_methods
|
18
|
+
@__package_with_methods ||= [].to_set
|
19
|
+
end
|
20
|
+
end # ClassMethods
|
21
|
+
|
22
|
+
##
|
23
|
+
# Returns the package data for the instance as a hash.
|
24
|
+
def package_data
|
25
|
+
Utils.sanitize(
|
26
|
+
self.class._package_with_methods.map { |meth| [meth, public_send(meth)] }.to_h
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def encoded_package_data
|
31
|
+
Base64.strict_encode64(Marshal.dump(package_data))
|
32
|
+
end
|
33
|
+
end # Mixin
|
34
|
+
end # Service::Subject
|
35
|
+
end # Ribbon::Intercom
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Ribbon::Intercom
|
2
|
+
class Service
|
3
|
+
module Channel::Stores
|
4
|
+
class MockStore < Store
|
5
|
+
def channels
|
6
|
+
@__channels ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def lock
|
10
|
+
@__lock ||= Mutex.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def token_exists?(token)
|
14
|
+
channels.key?(token)
|
15
|
+
end
|
16
|
+
|
17
|
+
def lookup_channel(token)
|
18
|
+
channels[token]
|
19
|
+
end
|
20
|
+
|
21
|
+
def persist(channel)
|
22
|
+
raise Errors::InvalidChannelError, channel.inspect unless channel.is_a?(Channel)
|
23
|
+
channels[channel.token] = channel
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(channel)
|
27
|
+
raise Errors::InvalidChannelError, channel.inspect unless channel.is_a?(Channel)
|
28
|
+
channels.delete(channel.token)
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def with_lock(channel, &block)
|
33
|
+
# This is a global lock, not a per-channel lock, but this store is only
|
34
|
+
# for testing so let's KISS.
|
35
|
+
lock.synchronize(&block)
|
36
|
+
end
|
37
|
+
end # RedisStore
|
38
|
+
end # Channel::Stores
|
39
|
+
end # Service
|
40
|
+
end # Ribbon::Intercom
|