connect_client 0.1.1 → 0.1.3
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/.gitignore +38 -38
- data/Gemfile +2 -2
- data/LICENSE +22 -22
- data/README.md +45 -45
- data/Rakefile +21 -21
- data/connect_client.gemspec +24 -24
- data/lib/connect_client.rb +40 -40
- data/lib/connect_client/client.rb +42 -42
- data/lib/connect_client/configuration.rb +11 -11
- data/lib/connect_client/event.rb +51 -51
- data/lib/connect_client/event_push_response.rb +48 -48
- data/lib/connect_client/http/event_endpoint.rb +138 -138
- data/lib/connect_client/version.rb +2 -2
- data/spec/connect_client/client_spec.rb +47 -47
- data/spec/connect_client/configuration_spec.rb +54 -54
- data/spec/connect_client/event_push_response_spec.rb +149 -149
- data/spec/connect_client/event_spec.rb +46 -46
- data/spec/connect_client/http/http_event_endpoint_spec.rb +120 -120
- data/spec/connect_client/http/synchrony/event_endpoint_spec.rb +59 -59
- data/spec/connect_client_spec.rb +24 -24
- metadata +1 -1
@@ -1,12 +1,12 @@
|
|
1
|
-
module ConnectClient
|
2
|
-
class Configuration
|
3
|
-
attr_accessor :base_url, :api_key, :project_id, :async
|
4
|
-
|
5
|
-
def initialize(api_key = '', project_id = '', async = false, base_url = 'https://api.getconnect.io')
|
6
|
-
@base_url = base_url
|
7
|
-
@api_key = api_key
|
8
|
-
@project_id = project_id
|
9
|
-
@async = async
|
10
|
-
end
|
11
|
-
end
|
1
|
+
module ConnectClient
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :base_url, :api_key, :project_id, :async
|
4
|
+
|
5
|
+
def initialize(api_key = '', project_id = '', async = false, base_url = 'https://api.getconnect.io')
|
6
|
+
@base_url = base_url
|
7
|
+
@api_key = api_key
|
8
|
+
@project_id = project_id
|
9
|
+
@async = async
|
10
|
+
end
|
11
|
+
end
|
12
12
|
end
|
data/lib/connect_client/event.rb
CHANGED
@@ -1,52 +1,52 @@
|
|
1
|
-
require 'securerandom'
|
2
|
-
require 'json'
|
3
|
-
require 'time'
|
4
|
-
|
5
|
-
module ConnectClient
|
6
|
-
class Event
|
7
|
-
|
8
|
-
@@RESERVED_PROPERTY_REGEX = /tp_.+/i
|
9
|
-
|
10
|
-
attr_reader :data
|
11
|
-
|
12
|
-
def initialize(data)
|
13
|
-
event_data_defaults = { id: SecureRandom.uuid, timestamp: Time.now.utc.iso8601 }
|
14
|
-
@data = event_data_defaults.merge(data)
|
15
|
-
|
16
|
-
if (@data[:timestamp].respond_to? :iso8601)
|
17
|
-
@data[:timestamp] = @data[:timestamp].iso8601
|
18
|
-
end
|
19
|
-
|
20
|
-
validate
|
21
|
-
end
|
22
|
-
|
23
|
-
def validate
|
24
|
-
invalid_properties = @data.keys.grep(@@RESERVED_PROPERTY_REGEX)
|
25
|
-
|
26
|
-
raise EventDataValidationError.new(invalid_properties) if invalid_properties.any?
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_json(options = nil)
|
30
|
-
@data.to_json
|
31
|
-
end
|
32
|
-
|
33
|
-
def to_s
|
34
|
-
"Event Data: #{@data}"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class EventDataValidationError < StandardError
|
39
|
-
attr_reader :invalid_property_names
|
40
|
-
|
41
|
-
def initialize(invalid_property_names)
|
42
|
-
@invalid_property_names = invalid_property_names
|
43
|
-
end
|
44
|
-
|
45
|
-
def message
|
46
|
-
messages = ['The following properties use the reserved prefix tp_:'] + @invalid_property_names.map do |property_name|
|
47
|
-
"->#{property_name}"
|
48
|
-
end
|
49
|
-
messages.join "\n"
|
50
|
-
end
|
51
|
-
end
|
1
|
+
require 'securerandom'
|
2
|
+
require 'json'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module ConnectClient
|
6
|
+
class Event
|
7
|
+
|
8
|
+
@@RESERVED_PROPERTY_REGEX = /tp_.+/i
|
9
|
+
|
10
|
+
attr_reader :data
|
11
|
+
|
12
|
+
def initialize(data)
|
13
|
+
event_data_defaults = { id: SecureRandom.uuid, timestamp: Time.now.utc.iso8601 }
|
14
|
+
@data = event_data_defaults.merge(data)
|
15
|
+
|
16
|
+
if (@data[:timestamp].respond_to? :iso8601)
|
17
|
+
@data[:timestamp] = @data[:timestamp].iso8601
|
18
|
+
end
|
19
|
+
|
20
|
+
validate
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate
|
24
|
+
invalid_properties = @data.keys.grep(@@RESERVED_PROPERTY_REGEX)
|
25
|
+
|
26
|
+
raise EventDataValidationError.new(invalid_properties) if invalid_properties.any?
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_json(options = nil)
|
30
|
+
@data.to_json
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
"Event Data: #{@data}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class EventDataValidationError < StandardError
|
39
|
+
attr_reader :invalid_property_names
|
40
|
+
|
41
|
+
def initialize(invalid_property_names)
|
42
|
+
@invalid_property_names = invalid_property_names
|
43
|
+
end
|
44
|
+
|
45
|
+
def message
|
46
|
+
messages = ['The following properties use the reserved prefix tp_:'] + @invalid_property_names.map do |property_name|
|
47
|
+
"->#{property_name}"
|
48
|
+
end
|
49
|
+
messages.join "\n"
|
50
|
+
end
|
51
|
+
end
|
52
52
|
end
|
@@ -1,49 +1,49 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
module ConnectClient
|
4
|
-
class EventPushResponse
|
5
|
-
attr_reader :data
|
6
|
-
attr_reader :http_status_code
|
7
|
-
|
8
|
-
def initialize(code, content_type, response_body, events_pushed)
|
9
|
-
@http_status_code = code.to_s
|
10
|
-
|
11
|
-
if content_type.include? 'application/json'
|
12
|
-
body = response_body
|
13
|
-
body = '{}' if response_body.to_s.empty?
|
14
|
-
parse_body(body, events_pushed)
|
15
|
-
else
|
16
|
-
@data = response_body
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def success?
|
21
|
-
@http_status_code.start_with? '2'
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_s
|
25
|
-
%{
|
26
|
-
Status: #{@http_status_code}
|
27
|
-
Successful: #{success?}
|
28
|
-
Data: #{data}
|
29
|
-
}
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def parse_body(body, events_pushed)
|
35
|
-
@data = JSON.parse(body, :symbolize_names => true)
|
36
|
-
|
37
|
-
if (events_pushed.is_a?(Hash) && @data.is_a?(Hash))
|
38
|
-
@data.merge!(events_pushed) do |collection_name, responses, events|
|
39
|
-
responses.zip(events).map do |response, event|
|
40
|
-
response[:event] = event.data
|
41
|
-
response
|
42
|
-
end
|
43
|
-
end
|
44
|
-
else
|
45
|
-
@data[:event] = events_pushed.data
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module ConnectClient
|
4
|
+
class EventPushResponse
|
5
|
+
attr_reader :data
|
6
|
+
attr_reader :http_status_code
|
7
|
+
|
8
|
+
def initialize(code, content_type, response_body, events_pushed)
|
9
|
+
@http_status_code = code.to_s
|
10
|
+
|
11
|
+
if content_type.include? 'application/json'
|
12
|
+
body = response_body
|
13
|
+
body = '{}' if response_body.to_s.empty?
|
14
|
+
parse_body(body, events_pushed)
|
15
|
+
else
|
16
|
+
@data = response_body
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def success?
|
21
|
+
@http_status_code.start_with? '2'
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
%{
|
26
|
+
Status: #{@http_status_code}
|
27
|
+
Successful: #{success?}
|
28
|
+
Data: #{data}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def parse_body(body, events_pushed)
|
35
|
+
@data = JSON.parse(body, :symbolize_names => true)
|
36
|
+
|
37
|
+
if (events_pushed.is_a?(Hash) && @data.is_a?(Hash))
|
38
|
+
@data.merge!(events_pushed) do |collection_name, responses, events|
|
39
|
+
responses.zip(events).map do |response, event|
|
40
|
+
response[:event] = event.data
|
41
|
+
response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@data[:event] = events_pushed.data
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
49
|
end
|
@@ -1,139 +1,139 @@
|
|
1
|
-
require 'json'
|
2
|
-
require_relative '../event_push_response'
|
3
|
-
|
4
|
-
module ConnectClient
|
5
|
-
module Http
|
6
|
-
class EventEndpoint
|
7
|
-
def initialize(config)
|
8
|
-
headers = {
|
9
|
-
"Content-Type" => "application/json",
|
10
|
-
"Accept" => "application/json",
|
11
|
-
"Accept-Encoding" => "identity",
|
12
|
-
"X-Api-Key" => config.api_key,
|
13
|
-
"X-Project-Id" => config.project_id
|
14
|
-
}
|
15
|
-
|
16
|
-
if config.async
|
17
|
-
@http = Async.new config.base_url, headers
|
18
|
-
else
|
19
|
-
@http = Sync.new config.base_url, headers
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
def push(collection_name, event)
|
25
|
-
path_uri_part = "/events/#{collection_name}"
|
26
|
-
|
27
|
-
@http.send path_uri_part, event.data.to_json, event
|
28
|
-
end
|
29
|
-
|
30
|
-
def push_batch(events_by_collection)
|
31
|
-
path_uri_part = "/events"
|
32
|
-
|
33
|
-
@http.send path_uri_part, events_by_collection.to_json, events_by_collection
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
class Sync
|
40
|
-
def initialize(base_url, headers)
|
41
|
-
require 'uri'
|
42
|
-
require 'net/http'
|
43
|
-
require 'net/https'
|
44
|
-
|
45
|
-
@headers = headers
|
46
|
-
@connect_uri = URI.parse(base_url)
|
47
|
-
@http = Net::HTTP.new(@connect_uri.host, @connect_uri.port)
|
48
|
-
setup_ssl if @connect_uri.scheme == 'https'
|
49
|
-
end
|
50
|
-
|
51
|
-
def send(path, body, events)
|
52
|
-
response = @http.post(path, body, @headers)
|
53
|
-
ConnectClient::EventPushResponse.new response.code, response['Content-Type'], response.body, events
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def setup_ssl
|
59
|
-
@http.use_ssl = true
|
60
|
-
@http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
61
|
-
@http.verify_depth = 5
|
62
|
-
@http.ca_file = File.expand_path("../../../../data/cacert.pem", __FILE__)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
class Async
|
67
|
-
|
68
|
-
def initialize(base_url, headers)
|
69
|
-
require 'em-http-request'
|
70
|
-
|
71
|
-
@headers = headers
|
72
|
-
@base_url = base_url.chomp('/')
|
73
|
-
end
|
74
|
-
|
75
|
-
def send(path, body, events)
|
76
|
-
raise AsyncHttpError unless defined?(EventMachine) && EventMachine.reactor_running?
|
77
|
-
|
78
|
-
use_syncrony = defined?(EM::Synchrony)
|
79
|
-
|
80
|
-
if use_syncrony
|
81
|
-
send_using_synchrony(path, body, events)
|
82
|
-
else
|
83
|
-
send_using_deferred(path, body, events)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def send_using_deferred(path, body, events)
|
88
|
-
deferred = DeferredHttpResponse.new
|
89
|
-
url_string = "#{@base_url}#{path}".chomp('/')
|
90
|
-
http = EventMachine::HttpRequest.new(url_string).post(:body => body, :head => @headers)
|
91
|
-
http_callback = Proc.new do
|
92
|
-
begin
|
93
|
-
response = create_response http, events
|
94
|
-
deferred.succeed response
|
95
|
-
rescue => error
|
96
|
-
deferred.fail error
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
http.callback &http_callback
|
101
|
-
http.errback &http_callback
|
102
|
-
|
103
|
-
deferred
|
104
|
-
end
|
105
|
-
|
106
|
-
def send_using_synchrony(path, body, events)
|
107
|
-
url_string = "#{@base_url}#{path}".chomp('/')
|
108
|
-
http = EventMachine::HttpRequest.new(url_string).
|
109
|
-
post(:body => body, :head => @headers)
|
110
|
-
|
111
|
-
create_response http, events
|
112
|
-
end
|
113
|
-
|
114
|
-
def create_response(http_reponse, events)
|
115
|
-
status = http_reponse.response_header.status
|
116
|
-
content_type = http_reponse.response_header['Content-Type']
|
117
|
-
if (http_reponse.error.to_s.empty?)
|
118
|
-
ConnectClient::EventPushResponse.new status, content_type, http_reponse.response, events
|
119
|
-
else
|
120
|
-
ConnectClient::EventPushResponse.new status, content_type, http_reponse.error, events
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
class DeferredHttpResponse
|
126
|
-
if defined?(EventMachine::Deferrable)
|
127
|
-
include EventMachine::Deferrable
|
128
|
-
alias_method :response_received, :callback
|
129
|
-
alias_method :error_occured, :errback
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
class AsyncHttpError < StandardError
|
134
|
-
def message
|
135
|
-
"An EventMachine loop must be running to send an async http request via 'em-http-request'"
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
1
|
+
require 'json'
|
2
|
+
require_relative '../event_push_response'
|
3
|
+
|
4
|
+
module ConnectClient
|
5
|
+
module Http
|
6
|
+
class EventEndpoint
|
7
|
+
def initialize(config)
|
8
|
+
headers = {
|
9
|
+
"Content-Type" => "application/json",
|
10
|
+
"Accept" => "application/json",
|
11
|
+
"Accept-Encoding" => "identity",
|
12
|
+
"X-Api-Key" => config.api_key,
|
13
|
+
"X-Project-Id" => config.project_id
|
14
|
+
}
|
15
|
+
|
16
|
+
if config.async
|
17
|
+
@http = Async.new config.base_url, headers
|
18
|
+
else
|
19
|
+
@http = Sync.new config.base_url, headers
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def push(collection_name, event)
|
25
|
+
path_uri_part = "/events/#{collection_name}"
|
26
|
+
|
27
|
+
@http.send path_uri_part, event.data.to_json, event
|
28
|
+
end
|
29
|
+
|
30
|
+
def push_batch(events_by_collection)
|
31
|
+
path_uri_part = "/events"
|
32
|
+
|
33
|
+
@http.send path_uri_part, events_by_collection.to_json, events_by_collection
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
class Sync
|
40
|
+
def initialize(base_url, headers)
|
41
|
+
require 'uri'
|
42
|
+
require 'net/http'
|
43
|
+
require 'net/https'
|
44
|
+
|
45
|
+
@headers = headers
|
46
|
+
@connect_uri = URI.parse(base_url)
|
47
|
+
@http = Net::HTTP.new(@connect_uri.host, @connect_uri.port)
|
48
|
+
setup_ssl if @connect_uri.scheme == 'https'
|
49
|
+
end
|
50
|
+
|
51
|
+
def send(path, body, events)
|
52
|
+
response = @http.post(path, body, @headers)
|
53
|
+
ConnectClient::EventPushResponse.new response.code, response['Content-Type'], response.body, events
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def setup_ssl
|
59
|
+
@http.use_ssl = true
|
60
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
61
|
+
@http.verify_depth = 5
|
62
|
+
@http.ca_file = File.expand_path("../../../../data/cacert.pem", __FILE__)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Async
|
67
|
+
|
68
|
+
def initialize(base_url, headers)
|
69
|
+
require 'em-http-request'
|
70
|
+
|
71
|
+
@headers = headers
|
72
|
+
@base_url = base_url.chomp('/')
|
73
|
+
end
|
74
|
+
|
75
|
+
def send(path, body, events)
|
76
|
+
raise AsyncHttpError unless defined?(EventMachine) && EventMachine.reactor_running?
|
77
|
+
|
78
|
+
use_syncrony = defined?(EM::Synchrony)
|
79
|
+
|
80
|
+
if use_syncrony
|
81
|
+
send_using_synchrony(path, body, events)
|
82
|
+
else
|
83
|
+
send_using_deferred(path, body, events)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def send_using_deferred(path, body, events)
|
88
|
+
deferred = DeferredHttpResponse.new
|
89
|
+
url_string = "#{@base_url}#{path}".chomp('/')
|
90
|
+
http = EventMachine::HttpRequest.new(url_string).post(:body => body, :head => @headers)
|
91
|
+
http_callback = Proc.new do
|
92
|
+
begin
|
93
|
+
response = create_response http, events
|
94
|
+
deferred.succeed response
|
95
|
+
rescue => error
|
96
|
+
deferred.fail error
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
http.callback &http_callback
|
101
|
+
http.errback &http_callback
|
102
|
+
|
103
|
+
deferred
|
104
|
+
end
|
105
|
+
|
106
|
+
def send_using_synchrony(path, body, events)
|
107
|
+
url_string = "#{@base_url}#{path}".chomp('/')
|
108
|
+
http = EventMachine::HttpRequest.new(url_string).
|
109
|
+
post(:body => body, :head => @headers)
|
110
|
+
|
111
|
+
create_response http, events
|
112
|
+
end
|
113
|
+
|
114
|
+
def create_response(http_reponse, events)
|
115
|
+
status = http_reponse.response_header.status
|
116
|
+
content_type = http_reponse.response_header['Content-Type']
|
117
|
+
if (http_reponse.error.to_s.empty?)
|
118
|
+
ConnectClient::EventPushResponse.new status, content_type, http_reponse.response, events
|
119
|
+
else
|
120
|
+
ConnectClient::EventPushResponse.new status, content_type, http_reponse.error, events
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class DeferredHttpResponse
|
126
|
+
if defined?(EventMachine::Deferrable)
|
127
|
+
include EventMachine::Deferrable
|
128
|
+
alias_method :response_received, :callback
|
129
|
+
alias_method :error_occured, :errback
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class AsyncHttpError < StandardError
|
134
|
+
def message
|
135
|
+
"An EventMachine loop must be running to send an async http request via 'em-http-request'"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
139
|
end
|