connect_client 0.2.2 → 0.3.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.
@@ -1,132 +1,136 @@
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
- require_relative 'deferred_http_response'
71
-
72
- @headers = headers
73
- @base_url = base_url.chomp('/')
74
- end
75
-
76
- def send(path, body, events)
77
- raise AsyncHttpError unless defined?(EventMachine) && EventMachine.reactor_running?
78
-
79
- use_syncrony = defined?(EM::Synchrony)
80
-
81
- if use_syncrony
82
- send_using_synchrony(path, body, events)
83
- else
84
- send_using_deferred(path, body, events)
85
- end
86
- end
87
-
88
- def send_using_deferred(path, body, events)
89
- deferred = DeferredHttpResponse.new
90
- url_string = "#{@base_url}#{path}".chomp('/')
91
- http = EventMachine::HttpRequest.new(url_string).post(:body => body, :head => @headers)
92
- http_callback = Proc.new do
93
- begin
94
- response = create_response http, events
95
- deferred.succeed response
96
- rescue => error
97
- deferred.fail error
98
- end
99
- end
100
-
101
- http.callback &http_callback
102
- http.errback &http_callback
103
-
104
- deferred
105
- end
106
-
107
- def send_using_synchrony(path, body, events)
108
- url_string = "#{@base_url}#{path}".chomp('/')
109
- http = EventMachine::HttpRequest.new(url_string).
110
- post(:body => body, :head => @headers)
111
-
112
- create_response http, events
113
- end
114
-
115
- def create_response(http_reponse, events)
116
- status = http_reponse.response_header.status
117
- content_type = http_reponse.response_header['Content-Type']
118
- if (http_reponse.error.to_s.empty?)
119
- ConnectClient::EventPushResponse.new status, content_type, http_reponse.response, events
120
- else
121
- ConnectClient::EventPushResponse.new status, content_type, http_reponse.error, events
122
- end
123
- end
124
- end
125
-
126
- class AsyncHttpError < StandardError
127
- def message
128
- "An EventMachine loop must be running to send an async http request via 'em-http-request'"
129
- end
130
- end
131
- end
1
+ require 'json'
2
+ require 'cgi'
3
+ require_relative '../event_push_response'
4
+
5
+ module ConnectClient
6
+ module Http
7
+ class EventEndpoint
8
+ def initialize(config)
9
+ headers = {
10
+ "Content-Type" => "application/json",
11
+ "Accept" => "application/json",
12
+ "Accept-Encoding" => "identity",
13
+ "X-Api-Key" => config.api_key,
14
+ "X-Project-Id" => config.project_id
15
+ }
16
+
17
+ if config.async
18
+ @http = EmHttp.new config.base_url, headers
19
+ else
20
+ @http = NetHttp.new config.base_url, headers
21
+ end
22
+
23
+ end
24
+
25
+ def push(collection_name, event)
26
+ path_uri_part = "/events/#{CGI.escape(collection_name.to_s)}"
27
+
28
+ @http.push_events path_uri_part, event.data.to_json, event
29
+ end
30
+
31
+ def push_batch(events_by_collection)
32
+ path_uri_part = "/events"
33
+
34
+ @http.push_events path_uri_part, events_by_collection.to_json, events_by_collection
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ class NetHttp
41
+ def initialize(base_url, headers)
42
+ require 'uri'
43
+ require 'net/http'
44
+ require 'net/https'
45
+
46
+ @headers = headers
47
+ @connect_uri = URI.parse(base_url)
48
+ @http = Net::HTTP.new(@connect_uri.host, @connect_uri.port)
49
+ setup_ssl if @connect_uri.scheme == 'https'
50
+ end
51
+
52
+ def push_events(path, body, events)
53
+ response = @http.post(path, body, @headers)
54
+ ConnectClient::EventPushResponse.new response.code, response['Content-Type'], response.body, events
55
+ end
56
+
57
+ private
58
+
59
+ def setup_ssl
60
+ root_ca = "#{ConnectClient::gem_root}/data/cacert.pem"
61
+ standard_depth = 5
62
+
63
+ @http.use_ssl = true
64
+ @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
65
+ @http.verify_depth = standard_depth
66
+ @http.ca_file = root_ca
67
+ end
68
+ end
69
+
70
+ class EmHttp
71
+
72
+ def initialize(base_url, headers)
73
+ require 'em-http-request'
74
+ require_relative 'deferred_http_response'
75
+
76
+ @headers = headers
77
+ @base_url = base_url.chomp('/')
78
+ end
79
+
80
+ def push_events(path, body, events)
81
+ raise AsyncHttpError unless defined?(EventMachine) && EventMachine.reactor_running?
82
+
83
+ use_syncrony = defined?(EM::Synchrony)
84
+
85
+ if use_syncrony
86
+ push_events_using_synchrony(path, body, events)
87
+ else
88
+ push_events_using_deferred(path, body, events)
89
+ end
90
+ end
91
+
92
+ def push_events_using_deferred(path, body, events)
93
+ deferred = DeferredHttpResponse.new
94
+ url_string = "#{@base_url}#{path}".chomp('/')
95
+ http = EventMachine::HttpRequest.new(url_string).post(:body => body, :head => @headers)
96
+ http_callback = Proc.new do
97
+ begin
98
+ response = create_response http, events
99
+ deferred.succeed response
100
+ rescue => error
101
+ deferred.fail error
102
+ end
103
+ end
104
+
105
+ http.callback &http_callback
106
+ http.errback &http_callback
107
+
108
+ deferred
109
+ end
110
+
111
+ def push_events_using_synchrony(path, body, events)
112
+ url_string = "#{@base_url}#{path}".chomp('/')
113
+ http = EventMachine::HttpRequest.new(url_string).
114
+ post(:body => body, :head => @headers)
115
+
116
+ create_response http, events
117
+ end
118
+
119
+ def create_response(http_reponse, events)
120
+ status = http_reponse.response_header.status
121
+ content_type = http_reponse.response_header['Content-Type']
122
+ if (http_reponse.error.to_s.empty?)
123
+ ConnectClient::EventPushResponse.new status, content_type, http_reponse.response, events
124
+ else
125
+ ConnectClient::EventPushResponse.new status, content_type, http_reponse.error, events
126
+ end
127
+ end
128
+ end
129
+
130
+ class AsyncHttpError < StandardError
131
+ def message
132
+ "You have tried to push events asynchronously without an event machine event loop running. The easiest way to do this is by passing a block to EM.run"
133
+ end
134
+ end
135
+ end
132
136
  end
@@ -0,0 +1,41 @@
1
+ require 'openssl'
2
+
3
+ module ConnectClient
4
+ module Security
5
+ def self.generate_filtered_key(key_json, master_key)
6
+ key = master_key
7
+ key = Digest::SHA256.digest(key) if(key.kind_of?(String) && 32 != key.bytesize)
8
+ aes = OpenSSL::Cipher.new('AES-256-CBC')
9
+ iv = aes.random_iv
10
+ aes.encrypt
11
+ aes.key = key
12
+ aes.iv = iv
13
+ encrypted = aes.update(key_json) + aes.final
14
+
15
+ "#{bin_to_hex(iv)}-#{bin_to_hex(encrypted)}"
16
+ end
17
+
18
+ def self.generate_key_json(filtered_key, master_key)
19
+ iv_and_data = filtered_key.split('-')
20
+ iv = hex_to_bin(iv_and_data[0])
21
+ encrypted_key_json = hex_to_bin(iv_and_data[1])
22
+
23
+ key = master_key
24
+ key = Digest::SHA256.digest(key) if(key.kind_of?(String) && 32 != key.bytesize)
25
+ iv = Digest::MD5.digest(iv) if(iv.kind_of?(String) && 16 != iv.bytesize)
26
+ aes = OpenSSL::Cipher.new('AES-256-CBC')
27
+ aes.decrypt
28
+ aes.key = key
29
+ aes.iv = iv
30
+ aes.update(encrypted_key_json) + aes.final
31
+ end
32
+
33
+ def self.bin_to_hex(binary_string)
34
+ binary_string.unpack("H*").first.to_s.upcase
35
+ end
36
+
37
+ def self.hex_to_bin(hex_string)
38
+ hex_string.scan(/../).map { |x| x.hex }.pack('c*')
39
+ end
40
+ end
41
+ end
@@ -1,3 +1,3 @@
1
1
  module ConnectClient
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -1,69 +1,69 @@
1
- require 'minitest/spec'
2
- require 'minitest/autorun'
3
- require 'webmock/minitest'
4
- require 'connect_client/client'
5
- require 'connect_client/event_push_response'
6
- require 'connect_client/configuration'
7
- require 'securerandom'
8
-
9
- describe ConnectClient::Client do
10
- before do
11
- @client = ConnectClient::Client.new (ConnectClient::Configuration.new)
12
- @async_client = ConnectClient::Client.new (ConnectClient::Configuration.new '', '', true)
13
- @sample_event = { id: SecureRandom.uuid, timestamp: Time.now.utc.iso8601, name: 'sample' }
14
- @sample_events = [@sample_event]
15
- @sample_events_reponse = '{"sample": [{"success": true}]}'
16
- @sample_collection = 'sample'
17
- @sample_collection_sym = :sample
18
- end
19
-
20
- it "should get a push response back when pushing a single event to a collection passed by string" do
21
- stub_request(:post, "https://api.getconnect.io/events/#{@sample_collection}").
22
- with(:body => @sample_event).
23
- to_return(:status => 200, :body => "", :headers => { 'Content-Type'=>'application/json' })
24
-
25
- response = @client.push @sample_collection, @sample_event
26
-
27
- response.must_be_instance_of ConnectClient::EventPushResponse
28
- end
29
-
30
- it "should get a push response back when pushing a single event to a collection passed by symbol" do
31
- stub_request(:post, "https://api.getconnect.io/events/#{@sample_collection}").
32
- with(:body => @sample_event).
33
- to_return(:status => 200, :body => "", :headers => { 'Content-Type'=>'application/json' })
34
-
35
- response = @client.push @sample_collection_sym, @sample_event
36
-
37
- response.must_be_instance_of ConnectClient::EventPushResponse
38
- end
39
-
40
- it "should get a push response back when pushing multiple events to a collection passed by string" do
41
- stub_request(:post, "https://api.getconnect.io/events").
42
- with(:body => { @sample_collection.to_sym => @sample_events }).
43
- to_return(:status => 200, :body => @sample_events_reponse, :headers => { 'Content-Type'=>'application/json' })
44
-
45
- response = @client.push @sample_collection, @sample_events
46
-
47
- response.must_be_instance_of ConnectClient::EventPushResponse
48
- end
49
-
50
- it "should get a push response back when pushing multiple events to a collection passed by symbol" do
51
- stub_request(:post, "https://api.getconnect.io/events").
52
- with(:body => { @sample_collection_sym => @sample_events }).
53
- to_return(:status => 200, :body => @sample_events_reponse, :headers => { 'Content-Type'=>'application/json' })
54
-
55
- response = @client.push @sample_collection_sym, @sample_events
56
-
57
- response.must_be_instance_of ConnectClient::EventPushResponse
58
- end
59
-
60
- it "should get a push response back when pushing batches" do
61
- batch = { @sample_collection.to_sym => @sample_events }
62
- stub_request(:post, "https://api.getconnect.io/events").
63
- with(:body => batch).
64
- to_return(:status => 200, :body => @sample_events_reponse, :headers => { 'Content-Type'=>'application/json' })
65
-
66
- response = @client.push batch
67
- response.must_be_instance_of ConnectClient::EventPushResponse
68
- end
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'webmock/minitest'
4
+ require 'connect_client/client'
5
+ require 'connect_client/event_push_response'
6
+ require 'connect_client/configuration'
7
+ require 'securerandom'
8
+
9
+ describe ConnectClient::Client do
10
+ before do
11
+ @client = ConnectClient::Client.new (ConnectClient::Configuration.new)
12
+ @async_client = ConnectClient::Client.new (ConnectClient::Configuration.new '', '', true)
13
+ @sample_event = { id: SecureRandom.uuid, timestamp: Time.now.utc.iso8601, name: 'sample' }
14
+ @sample_events = [@sample_event]
15
+ @sample_events_reponse = '{"sample": [{"success": true}]}'
16
+ @sample_collection = 'sample'
17
+ @sample_collection_sym = :sample
18
+ end
19
+
20
+ it "should get a push response back when pushing a single event to a collection passed by string" do
21
+ stub_request(:post, "https://api.getconnect.io/events/#{@sample_collection}").
22
+ with(:body => @sample_event).
23
+ to_return(:status => 200, :body => "", :headers => { 'Content-Type'=>'application/json' })
24
+
25
+ response = @client.push @sample_collection, @sample_event
26
+
27
+ response.must_be_instance_of ConnectClient::EventPushResponse
28
+ end
29
+
30
+ it "should get a push response back when pushing a single event to a collection passed by symbol" do
31
+ stub_request(:post, "https://api.getconnect.io/events/#{@sample_collection}").
32
+ with(:body => @sample_event).
33
+ to_return(:status => 200, :body => "", :headers => { 'Content-Type'=>'application/json' })
34
+
35
+ response = @client.push @sample_collection_sym, @sample_event
36
+
37
+ response.must_be_instance_of ConnectClient::EventPushResponse
38
+ end
39
+
40
+ it "should get a push response back when pushing multiple events to a collection passed by string" do
41
+ stub_request(:post, "https://api.getconnect.io/events").
42
+ with(:body => { @sample_collection.to_sym => @sample_events }).
43
+ to_return(:status => 200, :body => @sample_events_reponse, :headers => { 'Content-Type'=>'application/json' })
44
+
45
+ response = @client.push @sample_collection, @sample_events
46
+
47
+ response.must_be_instance_of ConnectClient::EventPushResponse
48
+ end
49
+
50
+ it "should get a push response back when pushing multiple events to a collection passed by symbol" do
51
+ stub_request(:post, "https://api.getconnect.io/events").
52
+ with(:body => { @sample_collection_sym => @sample_events }).
53
+ to_return(:status => 200, :body => @sample_events_reponse, :headers => { 'Content-Type'=>'application/json' })
54
+
55
+ response = @client.push @sample_collection_sym, @sample_events
56
+
57
+ response.must_be_instance_of ConnectClient::EventPushResponse
58
+ end
59
+
60
+ it "should get a push response back when pushing batches" do
61
+ batch = { @sample_collection.to_sym => @sample_events }
62
+ stub_request(:post, "https://api.getconnect.io/events").
63
+ with(:body => batch).
64
+ to_return(:status => 200, :body => @sample_events_reponse, :headers => { 'Content-Type'=>'application/json' })
65
+
66
+ response = @client.push batch
67
+ response.must_be_instance_of ConnectClient::EventPushResponse
68
+ end
69
69
  end