connect_client 0.2.2 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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