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

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.
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