evt-event_source-event_store-http 0.1.1.0 → 0.2.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/event_source/event_store/http.rb +10 -9
  3. data/lib/event_source/event_store/http/controls.rb +23 -3
  4. data/lib/event_source/event_store/http/controls/cluster_members.rb +9 -0
  5. data/lib/event_source/event_store/http/controls/event_data/write.rb +1 -0
  6. data/lib/event_source/event_store/http/controls/expected_version.rb +31 -0
  7. data/lib/event_source/event_store/http/controls/hostname.rb +13 -0
  8. data/lib/event_source/event_store/http/controls/hostname/cluster.rb +15 -0
  9. data/lib/event_source/event_store/http/controls/ip_address/cluster.rb +25 -0
  10. data/lib/event_source/event_store/http/controls/{category.rb → port.rb} +1 -1
  11. data/lib/event_source/event_store/http/controls/resolve_host.rb +9 -0
  12. data/lib/event_source/event_store/http/controls/session/request/require_leader.rb +41 -0
  13. data/lib/event_source/event_store/http/controls/session/request/write_event.rb +17 -0
  14. data/lib/event_source/event_store/http/controls/settings.rb +9 -0
  15. data/lib/event_source/event_store/http/controls/settings/cluster.rb +19 -0
  16. data/lib/event_source/event_store/http/controls/write.rb +9 -27
  17. data/lib/event_source/event_store/http/log.rb +0 -1
  18. data/lib/event_source/event_store/http/request.rb +27 -0
  19. data/lib/event_source/event_store/http/request/get.rb +64 -0
  20. data/lib/event_source/event_store/http/request/post.rb +79 -0
  21. data/lib/event_source/event_store/http/session.rb +63 -113
  22. data/lib/event_source/event_store/http/session/build.rb +28 -0
  23. data/lib/event_source/event_store/http/session/configure.rb +22 -0
  24. data/lib/event_source/event_store/http/session/defaults.rb +19 -0
  25. data/lib/event_source/event_store/http/session/log_text.rb +41 -0
  26. data/lib/event_source/event_store/http/session/substitute.rb +20 -30
  27. data/lib/event_source/event_store/http/session/telemetry.rb +61 -12
  28. data/lib/event_source/event_store/http/settings.rb +1 -21
  29. metadata +23 -21
  30. data/lib/event_source/event_store/http/session/net_http.rb +0 -100
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 64b31dd13ca19696703c311b5d8ea529b84e3a83
4
- data.tar.gz: 4fc28c6addd420dd2396eb61203c7d8256347f8f
3
+ metadata.gz: 6df13c6a49692d92c120afdb88275ad362527ef1
4
+ data.tar.gz: 409562ee8c2c8893f87e3bceeecd9966502e9bd7
5
5
  SHA512:
6
- metadata.gz: f8c0b1adf4c141191168395507e2ca23009758182b620d818a8ecd211fe379aa2fcd4d153a6484be3c179217e48d621bfa3fcb63535d643b814cc9564e435ba1
7
- data.tar.gz: 15ffe472c1372f7e3ca591c4e3d4e3641752a41bc38fdfde247205442962673118b33ae26e82551e3bb85f4eda40184e60b78f91e4dc4fd03c91a278bd39d540
6
+ metadata.gz: f83a2e956dbd796a73f69f368408496816d270b6d87b0d14e88af0f498c7eb5fb330f73a07c1e796d48815ba04e15bb8c439160982f17610c3d3e1b504a4871e
7
+ data.tar.gz: b82c62cf7c53332336722f1af0bed906ecd9efa2a3b0feec9e936da636a92bfbd0e580efe7747a3ba9e89b7fc8343b2dae214c06e0da1e6d2e41ae058a16a84e
@@ -1,17 +1,18 @@
1
- require 'net/http'
2
-
3
- require 'configure'; Configure.activate
4
1
  require 'event_source'
5
- require 'log'
6
- require 'settings'; Settings.activate
2
+ require 'event_store/cluster/leader_status'
7
3
 
8
4
  require 'event_source/event_store/http/log'
9
-
10
5
  require 'event_source/event_store/http/media_types'
6
+ require 'event_source/event_store/http/settings'
11
7
 
12
- require 'event_source/event_store/http/session/net_http'
13
- require 'event_source/event_store/http/session/telemetry'
8
+ require 'event_source/event_store/http/session/build'
9
+ require 'event_source/event_store/http/session/configure'
10
+ require 'event_source/event_store/http/session/defaults'
11
+ require 'event_source/event_store/http/session/log_text'
14
12
  require 'event_source/event_store/http/session/substitute'
13
+ require 'event_source/event_store/http/session/telemetry'
15
14
  require 'event_source/event_store/http/session'
16
15
 
17
- require 'event_source/event_store/http/settings'
16
+ require 'event_source/event_store/http/request'
17
+ require 'event_source/event_store/http/request/get'
18
+ require 'event_source/event_store/http/request/post'
@@ -1,13 +1,33 @@
1
1
  require 'identifier/uuid/controls'
2
2
  require 'event_source/controls'
3
+ require 'event_store/cluster/leader_status/controls'
4
+
5
+ require 'event_source/event_store/http/controls/uuid'
6
+
7
+ require 'event_source/event_store/http/controls/media_type'
8
+
9
+ require 'event_source/event_store/http/controls/resolve_host'
10
+
11
+ require 'event_source/event_store/http/controls/cluster_members'
12
+ require 'event_source/event_store/http/controls/hostname'
13
+ require 'event_source/event_store/http/controls/hostname/cluster'
14
+ require 'event_source/event_store/http/controls/ip_address/cluster'
15
+ require 'event_source/event_store/http/controls/port'
16
+
17
+ require 'event_source/event_store/http/controls/settings'
18
+ require 'event_source/event_store/http/controls/settings/cluster'
19
+
20
+ require 'event_source/event_store/http/controls/session/request/require_leader'
21
+ require 'event_source/event_store/http/controls/session/request/write_event'
3
22
 
4
- require 'event_source/event_store/http/controls/category'
5
23
  require 'event_source/event_store/http/controls/event_data'
6
24
  require 'event_source/event_store/http/controls/event_data/event_id'
7
25
  require 'event_source/event_store/http/controls/event_data/write'
8
- require 'event_source/event_store/http/controls/media_type'
26
+
9
27
  require 'event_source/event_store/http/controls/stream'
10
28
  require 'event_source/event_store/http/controls/stream_name'
11
29
  require 'event_source/event_store/http/controls/uri/path'
12
- require 'event_source/event_store/http/controls/uuid'
30
+
31
+ require 'event_source/event_store/http/controls/expected_version'
32
+
13
33
  require 'event_source/event_store/http/controls/write'
@@ -0,0 +1,9 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ module Controls
5
+ ClusterMembers = ::EventStore::Cluster::LeaderStatus::Controls::CurrentMembers
6
+ end
7
+ end
8
+ end
9
+ end
@@ -31,3 +31,4 @@ module EventSource
31
31
  end
32
32
  end
33
33
  end
34
+
@@ -0,0 +1,31 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ module Controls
5
+ module ExpectedVersion
6
+ def self.example
7
+ 11
8
+ end
9
+
10
+ module Header
11
+ def self.example
12
+ '11'
13
+ end
14
+ end
15
+
16
+ module NoStream
17
+ def self.example
18
+ :no_stream
19
+ end
20
+
21
+ module Header
22
+ def self.example
23
+ '-1'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ module Controls
5
+ module Hostname
6
+ def self.example
7
+ 'localhost'
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ module Controls
5
+ module Hostname
6
+ module Cluster
7
+ def self.example
8
+ 'localhost'
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ module Controls
5
+ module IPAddress
6
+ def self.example
7
+ '127.0.0.1'
8
+ end
9
+
10
+ Cluster = ::EventStore::Cluster::LeaderStatus::Controls::IPAddress
11
+
12
+ module Cluster
13
+ module Leader
14
+ def self.get
15
+ leader_ip_address, * = ::EventStore::Cluster::LeaderStatus::Controls::CurrentMembers.get
16
+
17
+ leader_ip_address
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -2,7 +2,7 @@ module EventSource
2
2
  module EventStore
3
3
  module HTTP
4
4
  module Controls
5
- Category = EventSource::Controls::Category
5
+ Port = ::EventStore::HTTP::Connect::Controls::Port
6
6
  end
7
7
  end
8
8
  end
@@ -0,0 +1,9 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ module Controls
5
+ ResolveHost = ::EventStore::HTTP::Connect::Controls::ResolveHost
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,41 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ module Controls
5
+ module Session
6
+ module Request
7
+ module WriteEvent
8
+ def self.example(data: nil, metadata: nil, type: nil, stream_name: nil, headers: nil)
9
+ stream_name ||= StreamName.example
10
+ headers ||= {}
11
+
12
+ event_data = EventData::Write.example(
13
+ type: type,
14
+ data: data,
15
+ metadata: metadata
16
+ )
17
+
18
+ request_body = JSON.pretty_generate [
19
+ {
20
+ 'eventId' => Identifier::UUID::Random.get,
21
+ 'eventType' => event_data.type,
22
+ 'data' => event_data.data,
23
+ 'metadata' => event_data.metadata
24
+ }
25
+ ]
26
+
27
+ headers['Content-Type'] ||= MediaTypes.vnd_event_store_events_json
28
+
29
+ path = "/streams/#{stream_name}"
30
+
31
+ post = Net::HTTP::Post.new path, headers
32
+ post.body = request_body
33
+ post
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,17 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ module Controls
5
+ module Session
6
+ module Request
7
+ module RequireLeader
8
+ def self.example
9
+ WriteEvent.example headers: { 'ES-RequireMaster' => 'true' }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ module Controls
5
+ Settings = ::EventStore::HTTP::Connect::Controls::Settings
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ module Controls
5
+ module Settings
6
+ module Cluster
7
+ def self.example
8
+ host = ::EventStore::Cluster::LeaderStatus::Controls::Hostname.example
9
+
10
+ HTTP::Settings.build({
11
+ :host => host
12
+ })
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -7,34 +7,15 @@ module EventSource
7
7
  event_count ||= 1
8
8
  stream_name ||= StreamName.example
9
9
 
10
- host = Settings.get :host
11
- port = Settings.get :port
10
+ ::EventStore::HTTP::Connect.() do |http|
11
+ post = Session::Request::WriteEvent.example(
12
+ type: type,
13
+ data: data,
14
+ metadata: metadata,
15
+ stream_name: stream_name
16
+ )
12
17
 
13
- event_data = EventData::Write.example(
14
- type: type,
15
- data: data,
16
- metadata: metadata
17
- )
18
-
19
- Net::HTTP.start host, port do |http|
20
- event_datum = (0...event_count).map do
21
- event_id = Identifier::UUID::Random.get
22
-
23
- {
24
- 'eventId' => event_id,
25
- 'eventType' => event_data.type,
26
- 'data' => event_data.data,
27
- 'metadata' => event_data.metadata
28
- }
29
- end
30
-
31
- headers = { 'Content-Type' => MediaTypes.vnd_event_store_events_json }
32
-
33
- path = "/streams/#{stream_name}"
34
-
35
- request_body = JSON.pretty_generate event_datum
36
-
37
- response = http.request_post path, request_body, headers
18
+ response = http.request post
38
19
 
39
20
  unless response.code.to_i == 201
40
21
  fail "Write failed (StatusCode: #{response.code}, ReasonPhrase: #{response.message})"
@@ -48,3 +29,4 @@ module EventSource
48
29
  end
49
30
  end
50
31
  end
32
+
@@ -4,7 +4,6 @@ module EventSource
4
4
  class Log < ::Log
5
5
  def tag!(tags)
6
6
  tags << :event_source_event_store_http
7
- tags << :library
8
7
  tags << :verbose
9
8
  end
10
9
  end
@@ -0,0 +1,27 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ class Request
5
+ include Log::Dependency
6
+
7
+ configure :request
8
+
9
+ dependency :session, Session
10
+
11
+ def self.build(session: nil)
12
+ instance = new
13
+ Session.configure instance, session: session
14
+ instance
15
+ end
16
+
17
+ abstract :call
18
+
19
+ abstract :media_type
20
+
21
+ def headers
22
+ @headers ||= {}
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,64 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ class Request
5
+ class Get < Request
6
+ include Log::Dependency
7
+
8
+ def call(path, &probe)
9
+ log_attributes = "Path: #{path}, MediaType: #{media_type}, Headers: #{headers.inspect}"
10
+
11
+ logger.trace { "Performing GET request (#{log_attributes}" }
12
+
13
+ request = Net::HTTP::Get.new path, headers
14
+ request['Accept'] = media_type
15
+
16
+ response = session.(request, &probe)
17
+
18
+ log_attributes << ", StatusCode: #{response.code}, ReasonPhrose: #{response.message}"
19
+
20
+ status_code = response.code.to_i
21
+
22
+ if (200..399).include? status_code
23
+ response_body = response.body
24
+ elsif status_code == 404
25
+ logger.warn "Get query failed, resource not found (#{log_attributes})"
26
+ else
27
+ error_message = "Get command failed (#{log_attributes})"
28
+ logger.error error_message
29
+ raise Error, error_message
30
+ end
31
+
32
+ logger.debug { "GET request done (#{log_attributes}, StatusCode: #{status_code})" }
33
+
34
+ return response_body, status_code
35
+ end
36
+
37
+ def enable_long_poll
38
+ headers['ES-LongPoll'] = Defaults.long_poll_duration.to_s
39
+ end
40
+
41
+ def long_poll_enabled?
42
+ headers.key? 'ES-LongPoll'
43
+ end
44
+
45
+ def media_type
46
+ MediaTypes.vnd_event_store_atom_json
47
+ end
48
+
49
+ Error = Class.new StandardError
50
+
51
+ module Defaults
52
+ def self.long_poll_duration
53
+ duration = ENV['LONG_POLL_DURATION']
54
+
55
+ return duration.to_i if duration
56
+
57
+ 15
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,79 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ class Request
5
+ class Post < Request
6
+ include Log::Dependency
7
+
8
+ def call(path, request_body, expected_version: nil, &probe)
9
+ expected_version = -1 if expected_version == Post.no_stream_version
10
+
11
+ log_attributes = "Path: #{path}, ContentLength: #{request_body.bytesize}, MediaType: #{media_type}, Headers: #{headers.inspect}"
12
+ logger.trace { "Performing GET request (#{log_attributes}" }
13
+
14
+ request = Net::HTTP::Post.new path, headers
15
+ request['Content-Type'] = media_type
16
+ request['ES-ExpectedVersion'] = expected_version.to_s if expected_version
17
+ request.body = request_body
18
+
19
+ response = session.(request, &probe)
20
+
21
+ status_code = response.code.to_i
22
+
23
+ log_attributes << ", StatusCode: #{status_code}, ReasonPhrase: #{response.message}"
24
+
25
+ unless (200..299).include? status_code
26
+ if expected_version_error? response
27
+ error_message = "Wrong expected version number (#{log_attributes})"
28
+ error_type = ExpectedVersionError
29
+ end
30
+
31
+ if write_timeout_error? response
32
+ error_message = "Write timeout (#{log_attributes})"
33
+ error_type = WriteTimeoutError
34
+ end
35
+
36
+ error_message ||= "Post command failed (#{log_attributes})"
37
+ error_type ||= Error
38
+
39
+ logger.error error_message
40
+ raise error_type, error_message
41
+ end
42
+
43
+ logger.debug { "GET request done (#{log_attributes}, StatusCode: #{status_code})" }
44
+
45
+ return status_code
46
+ end
47
+
48
+ def require_leader
49
+ headers['ES-RequireMaster'] = 'True'
50
+ end
51
+
52
+ def leader_required?
53
+ headers.key? 'ES-RequireMaster'
54
+ end
55
+
56
+ def media_type
57
+ MediaTypes.vnd_event_store_events_json
58
+ end
59
+
60
+ def expected_version_error?(response)
61
+ response.code == '400' && response.message == 'Wrong expected EventNumber'
62
+ end
63
+
64
+ def write_timeout_error?(response)
65
+ response.code == '500' && response.message == 'Write timeout'
66
+ end
67
+
68
+ Error = Class.new StandardError
69
+ ExpectedVersionError = Class.new Error
70
+ WriteTimeoutError = Class.new Error
71
+
72
+ def self.no_stream_version
73
+ :no_stream
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -4,157 +4,107 @@ module EventSource
4
4
  class Session
5
5
  include Log::Dependency
6
6
 
7
- dependency :net_http, NetHTTP
8
- dependency :telemetry, ::Telemetry
9
-
10
- def self.build(settings=nil, namespace: nil)
11
- instance = new
7
+ extend Build
8
+ extend Configure
9
+ extend Telemetry::RegisterSink
12
10
 
13
- ::Telemetry.configure instance
14
- NetHTTP.configure instance, settings: settings, namespace: namespace
11
+ attr_writer :connection
12
+ attr_writer :disable_leader_detection
15
13
 
16
- instance.connect
14
+ def disable_leader_detection
15
+ if @disable_leader_detection.nil?
16
+ @disable_leader_detection = Defaults.disable_leader_detection
17
+ end
17
18
 
18
- instance
19
+ @disable_leader_detection
19
20
  end
20
21
 
21
- def self.configure(receiver, settings=nil, namespace: nil, session: nil, attr_name: nil)
22
- attr_name ||= :session
22
+ dependency :connect, ::EventStore::HTTP::Connect
23
+ dependency :get_leader_status, ::EventStore::Cluster::LeaderStatus::Get
24
+ dependency :telemetry, ::Telemetry
25
+
26
+ attr_accessor :host
27
+ attr_accessor :port
23
28
 
24
- if session.nil?
25
- instance = build settings, namespace: namespace
26
- else
27
- instance = session
28
- end
29
+ def call(request, redirect: nil, &probe)
30
+ redirect ||= false
29
31
 
30
- receiver.public_send "#{attr_name}=", instance
32
+ logger.trace(tags: :http) { "Issuing #{request.method} request (#{LogText.request_attributes request})" }
33
+ logger.trace(tag: :data) { LogText.request_body request } if request.request_body_permitted?
31
34
 
32
- instance
33
- end
35
+ response = connection.request request
34
36
 
35
- def self.register_telemetry_sink(instance)
36
- sink = Telemetry::Sink.new
37
+ if Net::HTTPRedirection === response && !redirect
38
+ logger.debug(tags: :http) { "#{request.method} request received redirect response (#{LogText.request_attributes request}, #{LogText.response_attributes response}, Location: #{response['Location'] || '(none)'})" }
37
39
 
38
- instance.telemetry.register sink
40
+ location = URI.parse response['Location']
41
+ leader_ip_address = location.host
39
42
 
40
- sink
41
- end
43
+ telemetry.record :redirected, Telemetry::Redirected.new(request.path, connection.address, location)
42
44
 
43
- def connect
44
- logger.trace(tag: [:http, :db_connection]) {
45
- "Connecting to EventStore (Host: #{host.inspect}, Port: #{port.inspect})"
46
- }
45
+ establish_connection leader_ip_address
47
46
 
48
- net_http.start
47
+ request['Host'] = nil
48
+ response = self.(request, redirect: true)
49
49
 
50
- data = Telemetry::Connected.new host, port
50
+ return response
51
+ end
52
+
53
+ logger.trace(tags: :http) { "#{request.method} request issued (#{LogText.request_attributes request}, #{LogText.response_attributes response})" }
54
+ logger.trace(tag: :data) { LogText.response_body response }
51
55
 
52
- telemetry.record :connected, data
56
+ telemetry.record :http_request, Telemetry::HTTPRequest.build(request, response)
57
+
58
+ probe.(response) if probe
53
59
 
54
- logger.debug(tag: [:http, :db_connection]) {
55
- "Connected to EventStore (Host: #{host.inspect}, Port: #{port.inspect})"
56
- }
60
+ response
61
+ end
57
62
 
58
- net_http
63
+ def connection
64
+ @connection ||= establish_connection
59
65
  end
60
66
 
61
67
  def connected?
62
- net_http.started?
68
+ !@connection.nil?
63
69
  end
64
70
 
65
71
  def close
66
- logger.trace(tag: [:http, :db_connection]) { "Closing connection to EventStore" }
67
-
68
- net_http.finish
72
+ connection.finish
69
73
 
70
- logger.debug(tag: [:http, :db_connection]) { "Connection to EventStore closed" }
71
-
72
- net_http
74
+ self.connection = nil
73
75
  end
74
76
 
75
- def get(path, media_type, headers=nil, &probe)
76
- headers ||= {}
77
-
78
- logger.trace(tag: :http) {
79
- "Issuing GET request (Path: #{path}, MediaType: #{media_type})"
80
- }
77
+ def establish_connection(leader_ip_address=nil)
78
+ logger.trace { "Establishing connection to EventStore (#{LogText.establishing_connection self, leader_ip_address})" }
81
79
 
82
- headers['Accept'] = media_type
80
+ close if connected?
83
81
 
84
- response = net_http.request_get path, headers
82
+ unless leader_ip_address || disable_leader_detection
83
+ leader_status_queried_telemetry = Telemetry::LeaderStatusQueried.new
85
84
 
86
- status_code = response.code.to_i
87
- response_body = response.body if (200..399).include? status_code
85
+ begin
86
+ leader_status = get_leader_status.()
87
+ leader_ip_address = leader_status.http_ip_address
88
88
 
89
- logger.debug(tag: :http) {
90
- "GET request issued (Path: #{path}, MediaType: #{media_type}, StatusCode: #{status_code}, ReasonPhrase: #{response.message}, ContentLength: #{response_body&.bytesize.inspect})"
91
- }
89
+ leader_status_queried_telemetry.leader_status = leader_status
92
90
 
93
- if response.body.empty?
94
- logger.debug(tags: [:data]) { "Response: (none)" }
95
- else
96
- logger.debug(tags: [:data]) { "Response:\n\n#{response.body}" }
97
- end
98
-
99
- probe.(response) if probe
100
-
101
- data = Telemetry::Get.new(
102
- path,
103
- status_code,
104
- response.message,
105
- response.body,
106
- media_type
107
- )
108
-
109
- telemetry.record :get, data
110
-
111
- return status_code, response_body
112
- end
113
-
114
- def post(path, request_body, media_type, headers=nil, &probe)
115
- headers ||= {}
116
- headers['Content-Type'] = media_type
91
+ rescue ::EventStore::Cluster::LeaderStatus::GossipEndpoint::Get::NonClusterError => error
92
+ leader_status_queried_telemetry.error = error
93
+ logger.warn { "Could not determine cluster leader (#{LogText.establishing_connection self, leader_ip_address}, Error: #{error.class})" }
94
+ end
117
95
 
118
- logger.trace(tag: :http) {
119
- "Issuing POST request (Path: #{path}, MediaType: #{media_type}, ContentLength: #{request_body.bytesize})"
120
- }
121
- logger.trace(tags: [:data]) { "Request:\n\n#{request_body}" }
122
-
123
- response = net_http.request_post path, request_body, headers
124
-
125
- status_code = response.code.to_i
126
-
127
- logger.debug(tag: :http) {
128
- "POST request issued (Path: #{path}, MediaType: #{media_type}, ContentLength: #{request_body.bytesize}, StatusCode: #{status_code}, ReasonPhrase: #{response.message})"
129
- }
130
-
131
- if response.body.empty?
132
- logger.debug(tags: [:data]) { "Response: (none)" }
133
- else
134
- logger.debug(tags: [:data]) { "Response:\n\n#{response.body}" }
96
+ telemetry.record :leader_status_queried, leader_status_queried_telemetry
135
97
  end
136
98
 
137
- probe.(response) if probe
138
-
139
- data = Telemetry::Post.new(
140
- path,
141
- status_code,
142
- response.message,
143
- request_body,
144
- media_type
145
- )
99
+ connection = connect.(leader_ip_address)
146
100
 
147
- telemetry.record :post, data
101
+ telemetry.record :connection_established, Telemetry::ConnectionEstablished.new(leader_ip_address || host, port, connection)
148
102
 
149
- status_code
150
- end
103
+ logger.debug { "Connection to EventStore established (#{LogText.connection_established self, leader_ip_address})" }
151
104
 
152
- def host
153
- net_http.address
154
- end
105
+ self.connection = connection
155
106
 
156
- def port
157
- net_http.port
107
+ connection
158
108
  end
159
109
  end
160
110
  end
@@ -0,0 +1,28 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ class Session
5
+ module Build
6
+ def build(settings=nil, namespace: nil)
7
+ instance = new
8
+
9
+ connect = ::EventStore::HTTP::Connect.configure(
10
+ instance,
11
+ settings,
12
+ namespace: namespace
13
+ )
14
+
15
+ instance.host = connect.host
16
+ instance.port = connect.port
17
+
18
+ ::EventStore::Cluster::LeaderStatus::Get.configure instance, connect
19
+
20
+ ::Telemetry.configure instance
21
+
22
+ instance
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ class Session
5
+ module Configure
6
+ def configure(receiver, settings=nil, namespace: nil, session: nil, attr_name: nil)
7
+ attr_name ||= :session
8
+
9
+ if session.nil?
10
+ instance = build settings, namespace: namespace
11
+ else
12
+ instance = session
13
+ end
14
+
15
+ receiver.public_send "#{attr_name}=", instance
16
+ instance
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ class Session
5
+ module Defaults
6
+ def self.disable_leader_detection
7
+ disabled = ENV['DISABLE_EVENT_STORE_LEADER_DETECTION']
8
+
9
+ if /\A(?:on|yes|y|true|1)\z/i.match disabled
10
+ true
11
+ else
12
+ false
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ module EventSource
2
+ module EventStore
3
+ module HTTP
4
+ class Session
5
+ module LogText
6
+ def self.establishing_connection(session, leader_ip_address)
7
+ "HostSetting: #{session.host}, PortSetting: #{session.port}, LeaderIPAddress: #{leader_ip_address || '(unknown)'}"
8
+ end
9
+
10
+ def self.connection_established(session, leader_ip_address)
11
+ "HostSetting: #{session.host}, PortSetting: #{session.port}, LeaderIPAddress: #{leader_ip_address || '(unused)'}"
12
+ end
13
+
14
+ def self.request_attributes(request)
15
+ "Path: #{request.path}, Host: #{request['Host'] || '(not yet set)'}, MediaType: #{request['Content-Type'] || '(none)'}, ContentLength: #{request.body&.bytesize.to_i}, Accept: #{request['Accept'] || '(none)'}"
16
+ end
17
+
18
+ def self.request_body(request)
19
+ if request.body.nil? || request.body.empty?
20
+ "Request: (none)'"
21
+ else
22
+ "Request:\n\n#{request.body}"
23
+ end
24
+ end
25
+
26
+ def self.response_attributes(response)
27
+ "StatusCode: #{response.code}, ReasonPhrase: #{response.message}"
28
+ end
29
+
30
+ def self.response_body(response)
31
+ if response.body.empty?
32
+ "Response: (none)"
33
+ else
34
+ "Response:\n\n#{response.body}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -4,46 +4,36 @@ module EventSource
4
4
  class Session
5
5
  module Substitute
6
6
  def self.build
7
- Session.build
7
+ Session.new
8
8
  end
9
9
 
10
- class Session < Session
11
- attr_accessor :telemetry_sink
10
+ Response = Struct.new :code, :message, :body
12
11
 
13
- def self.build
14
- instance = new
12
+ class Session
13
+ attr_writer :status_code
14
+ attr_writer :reason_phrase
15
+ attr_accessor :response_body
15
16
 
16
- ::Telemetry.configure instance
17
-
18
- instance.telemetry_sink = register_telemetry_sink instance
19
-
20
- instance
17
+ def call(request)
18
+ response = Response.new
19
+ response.code = status_code.to_s
20
+ response.message = reason_phrase
21
+ response.body = response_body.to_s
22
+ response
21
23
  end
22
24
 
23
25
  def set_response(status_code, response_body=nil, reason_phrase: nil)
24
- net_http.set_response(
25
- status_code,
26
- response_body,
27
- reason_phrase: reason_phrase
28
- )
26
+ self.status_code = status_code
27
+ self.reason_phrase = reason_phrase
28
+ self.response_body = response_body if response_body
29
29
  end
30
30
 
31
- module Assertions
32
- def get_request?(&block)
33
- block ||= proc { true }
34
-
35
- telemetry_sink.recorded_get? do |record|
36
- block.(record.data)
37
- end
38
- end
39
-
40
- def post_request?(&block)
41
- block ||= proc { true }
31
+ def status_code
32
+ @status_code ||= 404
33
+ end
42
34
 
43
- telemetry_sink.recorded_post? do |record|
44
- block.(record.data)
45
- end
46
- end
35
+ def reason_phrase
36
+ @reason_phrase ||= 'Not found'
47
37
  end
48
38
  end
49
39
  end
@@ -6,28 +6,77 @@ module EventSource
6
6
  class Sink
7
7
  include ::Telemetry::Sink
8
8
 
9
- record :connected
10
- record :get
11
- record :post
9
+ record :connection_established
10
+
11
+ record :leader_status_queried
12
+
13
+ record :http_request
14
+
15
+ record :redirected
16
+
17
+ def leader_status_query_successful?
18
+ recorded_leader_status_queried? do |record|
19
+ record.data.leader_status && record.data.error.nil?
20
+ end
21
+ end
22
+
23
+ def leader_status_query_failed?
24
+ recorded_leader_status_queried? do |record|
25
+ record.data.leader_status.nil? && record.data.error
26
+ end
27
+ end
12
28
  end
13
29
 
14
- Connected = Struct.new :host, :port
30
+ module RegisterSink
31
+ def register_telemetry_sink(instance)
32
+ sink = Telemetry::Sink.new
15
33
 
16
- Get = Struct.new(
17
- :path,
18
- :status_code,
19
- :reason_phrase,
20
- :response_body,
21
- :acceptable_media_type
22
- )
34
+ instance.telemetry.register sink
23
35
 
24
- Post = Struct.new(
36
+ sink
37
+ end
38
+ end
39
+
40
+ ConnectionEstablished = Struct.new :host, :port, :connection
41
+
42
+ LeaderStatusQueried = Struct.new :leader_status, :error
43
+
44
+ HTTPRequest = Struct.new(
45
+ :request,
46
+ :response,
47
+ :action,
25
48
  :path,
26
49
  :status_code,
27
50
  :reason_phrase,
51
+ :response_body,
52
+ :acceptable_media_type,
28
53
  :request_body,
29
54
  :content_type
30
55
  )
56
+
57
+ class HTTPRequest
58
+ def self.build(request, response)
59
+ instance = new(
60
+ request,
61
+ response,
62
+ request.method,
63
+ request.path,
64
+ response.code.to_i,
65
+ response.message,
66
+ response.body,
67
+ request['Accept']
68
+ )
69
+
70
+ if request.request_body_permitted?
71
+ instance.request_body = request.body
72
+ instance.content_type = request['Content-Type']
73
+ end
74
+
75
+ instance
76
+ end
77
+ end
78
+
79
+ Redirected = Struct.new :requested_path, :origin_host, :redirect_uri
31
80
  end
32
81
  end
33
82
  end
@@ -1,27 +1,7 @@
1
1
  module EventSource
2
2
  module EventStore
3
3
  module HTTP
4
- class Settings < ::Settings
5
- def self.data_source
6
- 'settings/event_source_event_store_http.json'
7
- end
8
-
9
- def self.instance
10
- @instance ||= build
11
- end
12
-
13
- def self.names
14
- %i(host port read_timeout)
15
- end
16
-
17
- def self.set(receiver)
18
- instance.set receiver
19
- end
20
-
21
- def self.get(name)
22
- instance.get name
23
- end
24
- end
4
+ Settings = ::EventStore::HTTP::Connect::Settings
25
5
  end
26
6
  end
27
7
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evt-event_source-event_store-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1.0
4
+ version: 0.2.0.0.pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - The Eventide Project
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-22 00:00:00.000000000 Z
11
+ date: 2017-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: evt-configure
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: evt-event_source
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -39,7 +25,7 @@ dependencies:
39
25
  - !ruby/object:Gem::Version
40
26
  version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
- name: evt-settings
28
+ name: evt-event_store-cluster-leader_status
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
31
  - - ">="
@@ -74,11 +60,21 @@ extra_rdoc_files: []
74
60
  files:
75
61
  - lib/event_source/event_store/http.rb
76
62
  - lib/event_source/event_store/http/controls.rb
77
- - lib/event_source/event_store/http/controls/category.rb
63
+ - lib/event_source/event_store/http/controls/cluster_members.rb
78
64
  - lib/event_source/event_store/http/controls/event_data.rb
79
65
  - lib/event_source/event_store/http/controls/event_data/event_id.rb
80
66
  - lib/event_source/event_store/http/controls/event_data/write.rb
67
+ - lib/event_source/event_store/http/controls/expected_version.rb
68
+ - lib/event_source/event_store/http/controls/hostname.rb
69
+ - lib/event_source/event_store/http/controls/hostname/cluster.rb
70
+ - lib/event_source/event_store/http/controls/ip_address/cluster.rb
81
71
  - lib/event_source/event_store/http/controls/media_type.rb
72
+ - lib/event_source/event_store/http/controls/port.rb
73
+ - lib/event_source/event_store/http/controls/resolve_host.rb
74
+ - lib/event_source/event_store/http/controls/session/request/require_leader.rb
75
+ - lib/event_source/event_store/http/controls/session/request/write_event.rb
76
+ - lib/event_source/event_store/http/controls/settings.rb
77
+ - lib/event_source/event_store/http/controls/settings/cluster.rb
82
78
  - lib/event_source/event_store/http/controls/stream.rb
83
79
  - lib/event_source/event_store/http/controls/stream_name.rb
84
80
  - lib/event_source/event_store/http/controls/uri/path.rb
@@ -86,8 +82,14 @@ files:
86
82
  - lib/event_source/event_store/http/controls/write.rb
87
83
  - lib/event_source/event_store/http/log.rb
88
84
  - lib/event_source/event_store/http/media_types.rb
85
+ - lib/event_source/event_store/http/request.rb
86
+ - lib/event_source/event_store/http/request/get.rb
87
+ - lib/event_source/event_store/http/request/post.rb
89
88
  - lib/event_source/event_store/http/session.rb
90
- - lib/event_source/event_store/http/session/net_http.rb
89
+ - lib/event_source/event_store/http/session/build.rb
90
+ - lib/event_source/event_store/http/session/configure.rb
91
+ - lib/event_source/event_store/http/session/defaults.rb
92
+ - lib/event_source/event_store/http/session/log_text.rb
91
93
  - lib/event_source/event_store/http/session/substitute.rb
92
94
  - lib/event_source/event_store/http/session/telemetry.rb
93
95
  - lib/event_source/event_store/http/settings.rb
@@ -106,9 +108,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
106
108
  version: 2.3.3
107
109
  required_rubygems_version: !ruby/object:Gem::Requirement
108
110
  requirements:
109
- - - ">="
111
+ - - ">"
110
112
  - !ruby/object:Gem::Version
111
- version: '0'
113
+ version: 1.3.1
112
114
  requirements: []
113
115
  rubyforge_project:
114
116
  rubygems_version: 2.6.8
@@ -1,100 +0,0 @@
1
- module EventSource
2
- module EventStore
3
- module HTTP
4
- class Session
5
- module NetHTTP
6
- def self.configure(receiver, settings: nil, namespace: nil, attr_name: nil)
7
- attr_name ||= :net_http
8
- settings ||= Settings.instance
9
- namespace = Array(namespace)
10
-
11
- host = settings.get *namespace, :host
12
- port = settings.get *namespace, :port
13
- read_timeout = settings.get *namespace, :read_timeout
14
-
15
- net_http = Net::HTTP.new host, port
16
-
17
- net_http.read_timeout = read_timeout if read_timeout
18
-
19
- receiver.public_send "#{attr_name}=", net_http
20
-
21
- net_http
22
- end
23
-
24
- class Substitute
25
- attr_accessor :address
26
- attr_accessor :reason_phrase
27
- attr_writer :response_body
28
- attr_accessor :port
29
- attr_accessor :status_code
30
- attr_accessor :started
31
-
32
- def self.build
33
- new
34
- end
35
-
36
- def request_get(path, initheader=nil)
37
- status_code = get_request_status_code
38
-
39
- OpenStruct.new(
40
- :code => status_code,
41
- :body => response_body,
42
- :message => reason_phrase
43
- )
44
- end
45
-
46
- def request_post(path, request_body, initheader=nil)
47
- status_code = post_request_status_code
48
-
49
- OpenStruct.new(
50
- :code => status_code,
51
- :body => response_body,
52
- :message => reason_phrase
53
- )
54
- end
55
-
56
- def start
57
- self.started = true
58
- end
59
-
60
- def started?
61
- started ? true : false
62
- end
63
-
64
- def response_body
65
- @response_body ||= Defaults.response_body
66
- end
67
-
68
- def get_request_status_code
69
- status_code || Defaults.get_request_status_code
70
- end
71
-
72
- def post_request_status_code
73
- status_code || Defaults.post_request_status_code
74
- end
75
-
76
- def set_response(status_code, response_body=nil, reason_phrase: nil)
77
- self.status_code = status_code
78
- self.response_body = response_body if response_body
79
- self.reason_phrase = reason_phrase if reason_phrase
80
- end
81
-
82
- module Defaults
83
- def self.get_request_status_code
84
- 404
85
- end
86
-
87
- def self.post_request_status_code
88
- 201
89
- end
90
-
91
- def self.response_body
92
- ''
93
- end
94
- end
95
- end
96
- end
97
- end
98
- end
99
- end
100
- end