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.
- checksums.yaml +4 -4
- data/lib/event_source/event_store/http.rb +10 -9
- data/lib/event_source/event_store/http/controls.rb +23 -3
- data/lib/event_source/event_store/http/controls/cluster_members.rb +9 -0
- data/lib/event_source/event_store/http/controls/event_data/write.rb +1 -0
- data/lib/event_source/event_store/http/controls/expected_version.rb +31 -0
- data/lib/event_source/event_store/http/controls/hostname.rb +13 -0
- data/lib/event_source/event_store/http/controls/hostname/cluster.rb +15 -0
- data/lib/event_source/event_store/http/controls/ip_address/cluster.rb +25 -0
- data/lib/event_source/event_store/http/controls/{category.rb → port.rb} +1 -1
- data/lib/event_source/event_store/http/controls/resolve_host.rb +9 -0
- data/lib/event_source/event_store/http/controls/session/request/require_leader.rb +41 -0
- data/lib/event_source/event_store/http/controls/session/request/write_event.rb +17 -0
- data/lib/event_source/event_store/http/controls/settings.rb +9 -0
- data/lib/event_source/event_store/http/controls/settings/cluster.rb +19 -0
- data/lib/event_source/event_store/http/controls/write.rb +9 -27
- data/lib/event_source/event_store/http/log.rb +0 -1
- data/lib/event_source/event_store/http/request.rb +27 -0
- data/lib/event_source/event_store/http/request/get.rb +64 -0
- data/lib/event_source/event_store/http/request/post.rb +79 -0
- data/lib/event_source/event_store/http/session.rb +63 -113
- data/lib/event_source/event_store/http/session/build.rb +28 -0
- data/lib/event_source/event_store/http/session/configure.rb +22 -0
- data/lib/event_source/event_store/http/session/defaults.rb +19 -0
- data/lib/event_source/event_store/http/session/log_text.rb +41 -0
- data/lib/event_source/event_store/http/session/substitute.rb +20 -30
- data/lib/event_source/event_store/http/session/telemetry.rb +61 -12
- data/lib/event_source/event_store/http/settings.rb +1 -21
- metadata +23 -21
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6df13c6a49692d92c120afdb88275ad362527ef1
|
4
|
+
data.tar.gz: 409562ee8c2c8893f87e3bceeecd9966502e9bd7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 '
|
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/
|
13
|
-
require 'event_source/event_store/http/session/
|
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/
|
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
|
-
|
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
|
-
|
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,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,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
|
@@ -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,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
|
-
|
11
|
-
|
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
|
-
|
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
|
+
|
@@ -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
|
-
|
8
|
-
|
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
|
-
|
14
|
-
|
11
|
+
attr_writer :connection
|
12
|
+
attr_writer :disable_leader_detection
|
15
13
|
|
16
|
-
|
14
|
+
def disable_leader_detection
|
15
|
+
if @disable_leader_detection.nil?
|
16
|
+
@disable_leader_detection = Defaults.disable_leader_detection
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
+
@disable_leader_detection
|
19
20
|
end
|
20
21
|
|
21
|
-
|
22
|
-
|
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
|
-
|
25
|
-
|
26
|
-
else
|
27
|
-
instance = session
|
28
|
-
end
|
29
|
+
def call(request, redirect: nil, &probe)
|
30
|
+
redirect ||= false
|
29
31
|
|
30
|
-
|
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
|
-
|
33
|
-
end
|
35
|
+
response = connection.request request
|
34
36
|
|
35
|
-
|
36
|
-
|
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
|
-
|
40
|
+
location = URI.parse response['Location']
|
41
|
+
leader_ip_address = location.host
|
39
42
|
|
40
|
-
|
41
|
-
end
|
43
|
+
telemetry.record :redirected, Telemetry::Redirected.new(request.path, connection.address, location)
|
42
44
|
|
43
|
-
|
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
|
-
|
47
|
+
request['Host'] = nil
|
48
|
+
response = self.(request, redirect: true)
|
49
49
|
|
50
|
-
|
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 :
|
56
|
+
telemetry.record :http_request, Telemetry::HTTPRequest.build(request, response)
|
57
|
+
|
58
|
+
probe.(response) if probe
|
53
59
|
|
54
|
-
|
55
|
-
|
56
|
-
}
|
60
|
+
response
|
61
|
+
end
|
57
62
|
|
58
|
-
|
63
|
+
def connection
|
64
|
+
@connection ||= establish_connection
|
59
65
|
end
|
60
66
|
|
61
67
|
def connected?
|
62
|
-
|
68
|
+
!@connection.nil?
|
63
69
|
end
|
64
70
|
|
65
71
|
def close
|
66
|
-
|
67
|
-
|
68
|
-
net_http.finish
|
72
|
+
connection.finish
|
69
73
|
|
70
|
-
|
71
|
-
|
72
|
-
net_http
|
74
|
+
self.connection = nil
|
73
75
|
end
|
74
76
|
|
75
|
-
def
|
76
|
-
|
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
|
-
|
80
|
+
close if connected?
|
83
81
|
|
84
|
-
|
82
|
+
unless leader_ip_address || disable_leader_detection
|
83
|
+
leader_status_queried_telemetry = Telemetry::LeaderStatusQueried.new
|
85
84
|
|
86
|
-
|
87
|
-
|
85
|
+
begin
|
86
|
+
leader_status = get_leader_status.()
|
87
|
+
leader_ip_address = leader_status.http_ip_address
|
88
88
|
|
89
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
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
|
-
|
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 :
|
101
|
+
telemetry.record :connection_established, Telemetry::ConnectionEstablished.new(leader_ip_address || host, port, connection)
|
148
102
|
|
149
|
-
|
150
|
-
end
|
103
|
+
logger.debug { "Connection to EventStore established (#{LogText.connection_established self, leader_ip_address})" }
|
151
104
|
|
152
|
-
|
153
|
-
net_http.address
|
154
|
-
end
|
105
|
+
self.connection = connection
|
155
106
|
|
156
|
-
|
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.
|
7
|
+
Session.new
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
attr_accessor :telemetry_sink
|
10
|
+
Response = Struct.new :code, :message, :body
|
12
11
|
|
13
|
-
|
14
|
-
|
12
|
+
class Session
|
13
|
+
attr_writer :status_code
|
14
|
+
attr_writer :reason_phrase
|
15
|
+
attr_accessor :response_body
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
44
|
-
|
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 :
|
10
|
-
|
11
|
-
record :
|
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
|
-
|
30
|
+
module RegisterSink
|
31
|
+
def register_telemetry_sink(instance)
|
32
|
+
sink = Telemetry::Sink.new
|
15
33
|
|
16
|
-
|
17
|
-
:path,
|
18
|
-
:status_code,
|
19
|
-
:reason_phrase,
|
20
|
-
:response_body,
|
21
|
-
:acceptable_media_type
|
22
|
-
)
|
34
|
+
instance.telemetry.register sink
|
23
35
|
|
24
|
-
|
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
|
-
|
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.
|
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:
|
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-
|
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/
|
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/
|
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:
|
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
|