event_store_http_client 0.0.0
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 +7 -0
- data/lib/event_store_http_client.rb +17 -0
- data/lib/event_store_http_client/client/builder.rb +31 -0
- data/lib/event_store_http_client/events/read.rb +69 -0
- data/lib/event_store_http_client/events/write.rb +90 -0
- data/lib/event_store_http_client/handler.rb +49 -0
- data/lib/event_store_http_client/projection/get_state.rb +70 -0
- data/lib/event_store_http_client/settings.rb +9 -0
- data/lib/event_store_http_client/subscriptions/subscribe.rb +109 -0
- data/lib/event_store_http_client/writer.rb +11 -0
- data/lib/retry.rb +15 -0
- metadata +52 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: e15462829913d70b397c5b8fce0dcdd0bbed6cf2
|
|
4
|
+
data.tar.gz: feb551b2271a10390f4546c75929de60074163d9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6e610073c38d950815118fc81b16f5e42dbf987fffd893ebf5f43ed2707efeb3fd138b5a4c425a7243a56b0694d1c34728a8a5310d93e45f86ddb67db58363e1
|
|
7
|
+
data.tar.gz: 87a6744712ae80d053eff960b3dbadc09a7bceacac64d9724c7918105188a4b08a596d18a69115c1e98de137ee16c7f5a755e74e5c935e5771f1acefebea1c70
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'dependency'
|
|
2
|
+
Dependency.activate
|
|
3
|
+
|
|
4
|
+
require 'logger'
|
|
5
|
+
require 'uuid'
|
|
6
|
+
require 'settings'
|
|
7
|
+
Settings.activate
|
|
8
|
+
|
|
9
|
+
require 'retry'
|
|
10
|
+
require 'event_store_http_client/settings'
|
|
11
|
+
require 'event_store_http_client/handler'
|
|
12
|
+
require 'event_store_http_client/client/builder'
|
|
13
|
+
require 'event_store_http_client/writer'
|
|
14
|
+
require 'event_store_http_client/events/read'
|
|
15
|
+
require 'event_store_http_client/events/write'
|
|
16
|
+
require 'event_store_http_client/subscriptions/subscribe'
|
|
17
|
+
require 'event_store_http_client/projection/get_state'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module EventStore
|
|
2
|
+
module HTTPClient
|
|
3
|
+
module Client
|
|
4
|
+
class Builder
|
|
5
|
+
|
|
6
|
+
setting :host
|
|
7
|
+
setting :port
|
|
8
|
+
|
|
9
|
+
def self.build
|
|
10
|
+
new.tap do |instance|
|
|
11
|
+
Settings.instance.set instance, 'event_store_connection'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.configure(receiver)
|
|
16
|
+
instance = build
|
|
17
|
+
logger = Logger.get self
|
|
18
|
+
logger.trace "Configuring Client"
|
|
19
|
+
|
|
20
|
+
client = Vertx::HttpClient.new.tap do |client|
|
|
21
|
+
client.host = instance.host
|
|
22
|
+
client.port = instance.port
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
receiver.client = client
|
|
26
|
+
client
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module EventStore
|
|
2
|
+
module HTTPClient
|
|
3
|
+
module Events
|
|
4
|
+
class Read
|
|
5
|
+
|
|
6
|
+
attr_accessor :stream_name
|
|
7
|
+
attr_accessor :body
|
|
8
|
+
|
|
9
|
+
dependency :client
|
|
10
|
+
dependency :logger, Logger
|
|
11
|
+
|
|
12
|
+
def self.!(params)
|
|
13
|
+
instance = build(params)
|
|
14
|
+
instance.! do |result|
|
|
15
|
+
yield result
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.build(params)
|
|
20
|
+
body = params[:body]
|
|
21
|
+
stream_name = params[:stream_name]
|
|
22
|
+
|
|
23
|
+
new(stream_name).tap do |instance|
|
|
24
|
+
Logger.configure instance
|
|
25
|
+
EventStore::HTTPClient::Client::Builder.configure instance
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(stream_name)
|
|
30
|
+
@stream_name = stream_name
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def !
|
|
34
|
+
make_request do |result|
|
|
35
|
+
yield result
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def make_request
|
|
40
|
+
logger.debug "Making request to #{stream_name}"
|
|
41
|
+
request = client.get("/streams/#{stream_name}?embed=body") do |resp|
|
|
42
|
+
logger.debug "Response #{resp.status_code}"
|
|
43
|
+
|
|
44
|
+
resp.body_handler do |body|
|
|
45
|
+
status = resp.status_code == 200 ? :success : :error
|
|
46
|
+
yield({ status: status,
|
|
47
|
+
status_code: resp.status_code,
|
|
48
|
+
body: body.to_s})
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
request.put_header('Accept', 'application/vnd.eventstore.atom+json')
|
|
53
|
+
request.put_header('Content-Type', 'application/json')
|
|
54
|
+
|
|
55
|
+
request.exception_handler { |e|
|
|
56
|
+
logger.error "Failed to read, trying again"
|
|
57
|
+
Vertx.set_timer(rand(1000)) do
|
|
58
|
+
make_request do |result|
|
|
59
|
+
yield result
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
request.end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module EventStore
|
|
2
|
+
module HTTPClient
|
|
3
|
+
module Events
|
|
4
|
+
class Write
|
|
5
|
+
|
|
6
|
+
attr_accessor :data
|
|
7
|
+
attr_accessor :id
|
|
8
|
+
attr_accessor :stream_name
|
|
9
|
+
attr_accessor :location
|
|
10
|
+
attr_accessor :version
|
|
11
|
+
attr_accessor :type
|
|
12
|
+
|
|
13
|
+
dependency :logger, Logger
|
|
14
|
+
dependency :settings
|
|
15
|
+
dependency :client
|
|
16
|
+
|
|
17
|
+
def self.!(params)
|
|
18
|
+
instance = build(params)
|
|
19
|
+
instance.! do |result|
|
|
20
|
+
yield result if block_given?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.build(params)
|
|
25
|
+
type = params[:type]
|
|
26
|
+
version = params[:version]
|
|
27
|
+
data = params[:data].to_json
|
|
28
|
+
stream_name = params[:stream_name]
|
|
29
|
+
|
|
30
|
+
new(type, data, stream_name, version).tap do |instance|
|
|
31
|
+
Logger.configure instance
|
|
32
|
+
EventStore::HTTPClient::Client::Builder.configure instance
|
|
33
|
+
instance.id = UUID::Random.get
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def initialize(type, data, stream_name, version)
|
|
38
|
+
@type = type
|
|
39
|
+
@data = data
|
|
40
|
+
@stream_name = stream_name
|
|
41
|
+
@version = version
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def !
|
|
45
|
+
make_request do |result|
|
|
46
|
+
yield result
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def make_request
|
|
51
|
+
logger.debug "Making request to #{stream_name}"
|
|
52
|
+
request = client.post("/streams/#{stream_name}") do |resp|
|
|
53
|
+
logger.debug "Response #{resp.status_code}"
|
|
54
|
+
|
|
55
|
+
if resp.status_code == 201
|
|
56
|
+
yield :success if block_given?
|
|
57
|
+
else
|
|
58
|
+
yield resp.status_code if block_given?
|
|
59
|
+
end
|
|
60
|
+
resp.body_handler do |body|
|
|
61
|
+
# puts "The total body received was #{body.length} bytes"
|
|
62
|
+
# puts body
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
request.put_header('ES-EventType', type)
|
|
67
|
+
request.put_header("ES-EventId", id)
|
|
68
|
+
request.put_header("ES-ExpectedVersion", version) if version
|
|
69
|
+
request.put_header('Accept', 'application/vnd.eventstore.atom+json')
|
|
70
|
+
request.put_header('Content-Length', data.length)
|
|
71
|
+
request.put_header('Content-Type', 'application/json')
|
|
72
|
+
|
|
73
|
+
request.exception_handler { |e|
|
|
74
|
+
logger.error "Event #{id} failed to write, trying again"
|
|
75
|
+
logger.error e
|
|
76
|
+
Vertx.set_timer(rand(1000)+10) do
|
|
77
|
+
make_request do |result|
|
|
78
|
+
yield result
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
request.write_str(data)
|
|
84
|
+
|
|
85
|
+
request.end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module EventStore
|
|
2
|
+
module HTTPClient
|
|
3
|
+
module Handler
|
|
4
|
+
def self.included(cls)
|
|
5
|
+
cls.send :dependency, :logger, Logger
|
|
6
|
+
cls.extend Build unless cls.ancestors.include? Build
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def configure(receiver)
|
|
11
|
+
receiver.handler = self
|
|
12
|
+
self
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def !(event, attempt=0)
|
|
16
|
+
logger.trace "Event sourced: #{event}"
|
|
17
|
+
command = command(event)
|
|
18
|
+
|
|
19
|
+
logger.trace "Executing #{command.class} on #{event}"
|
|
20
|
+
command.!
|
|
21
|
+
logger.debug "Executed #{command.class}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module Build
|
|
25
|
+
def build
|
|
26
|
+
new.tap do |instance|
|
|
27
|
+
Logger.configure instance
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class NullCommand
|
|
33
|
+
dependency :logger, Logger
|
|
34
|
+
attr_accessor :event_type
|
|
35
|
+
|
|
36
|
+
def !
|
|
37
|
+
logger.debug "No command to execute for this eventType (type: #{event_type})"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.build(event_type)
|
|
41
|
+
new.tap do |instance|
|
|
42
|
+
Logger.configure instance
|
|
43
|
+
instance.event_type = event_type
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module EventStore
|
|
2
|
+
module HTTPClient
|
|
3
|
+
module Projection
|
|
4
|
+
class GetState
|
|
5
|
+
|
|
6
|
+
attr_accessor :projection
|
|
7
|
+
attr_accessor :partition
|
|
8
|
+
|
|
9
|
+
dependency :client
|
|
10
|
+
dependency :logger, Logger
|
|
11
|
+
|
|
12
|
+
def self.!(params)
|
|
13
|
+
instance = build(params)
|
|
14
|
+
instance.! do |result|
|
|
15
|
+
yield result
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.build(params)
|
|
20
|
+
projection = params[:projection]
|
|
21
|
+
partition = params[:partition]
|
|
22
|
+
|
|
23
|
+
new(projection, partition).tap do |instance|
|
|
24
|
+
Logger.configure instance
|
|
25
|
+
EventStore::HTTPClient::Client::Builder.configure instance
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(projection, partition)
|
|
30
|
+
@projection = projection
|
|
31
|
+
@partition = partition
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def !
|
|
35
|
+
make_request do |result|
|
|
36
|
+
yield result
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def make_request
|
|
41
|
+
logger.debug "Making request to /projection/#{projection}/state?partition=#{partition}"
|
|
42
|
+
request = client.get("/projection/#{projection}/state?partition=#{partition}") do |resp|
|
|
43
|
+
logger.debug "Response #{resp.status_code}"
|
|
44
|
+
|
|
45
|
+
resp.body_handler do |body|
|
|
46
|
+
status = resp.status_code == 200 ? :success : :error
|
|
47
|
+
yield({ status: status,
|
|
48
|
+
status_code: resp.status_code,
|
|
49
|
+
body: body})
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# If I put this 'Accept' header in, it returns a 406. Else, it works
|
|
54
|
+
# request.put_header('Accept', 'application/vnd.eventstore.atom+json')
|
|
55
|
+
request.put_header('Content-Type', 'application/json')
|
|
56
|
+
|
|
57
|
+
request.exception_handler { |e|
|
|
58
|
+
logger.error "Projection failed to query state, trying again"
|
|
59
|
+
Vertx.set_timer(rand(1000)) do
|
|
60
|
+
make_request
|
|
61
|
+
end
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
request.end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
module EventStore
|
|
2
|
+
module HTTPClient
|
|
3
|
+
|
|
4
|
+
def self.subscribe(starting_point, stream, handler)
|
|
5
|
+
Subscriptions::Subscribe.!(
|
|
6
|
+
starting_point: starting_point,
|
|
7
|
+
stream: stream,
|
|
8
|
+
handler: handler
|
|
9
|
+
)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module Subscriptions
|
|
13
|
+
class Subscribe
|
|
14
|
+
|
|
15
|
+
attr_accessor :stream
|
|
16
|
+
attr_accessor :starting_point
|
|
17
|
+
attr_accessor :request_string
|
|
18
|
+
attr_accessor :handler
|
|
19
|
+
|
|
20
|
+
dependency :client
|
|
21
|
+
dependency :logger, Logger
|
|
22
|
+
|
|
23
|
+
def self.!(params)
|
|
24
|
+
instance = build(params)
|
|
25
|
+
instance.!
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.build(params)
|
|
29
|
+
starting_point = params[:starting_point]
|
|
30
|
+
stream = params[:stream]
|
|
31
|
+
handler = params[:handler]
|
|
32
|
+
new(stream, starting_point).tap do |instance|
|
|
33
|
+
handler.configure instance
|
|
34
|
+
EventStore::HTTPClient::Client::Builder.configure instance
|
|
35
|
+
Logger.configure instance
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def initialize(stream, starting_point)
|
|
40
|
+
@stream = stream
|
|
41
|
+
@starting_point = starting_point
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def !
|
|
45
|
+
logger.info "Starting from #{starting_point}"
|
|
46
|
+
@request_string = "/streams/#{stream}/#{starting_point}/forward/20"
|
|
47
|
+
make_request
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def make_request
|
|
51
|
+
body_embed_link = "#{request_string}?embed=body"
|
|
52
|
+
|
|
53
|
+
logger.debug body_embed_link
|
|
54
|
+
|
|
55
|
+
request = client.get(body_embed_link) do |resp|
|
|
56
|
+
resp.body_handler = body_handler
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
request.put_header('Accept', 'application/vnd.eventstore.atom+json')
|
|
60
|
+
request.put_header('ES-LongPoll', 15)
|
|
61
|
+
|
|
62
|
+
request.exception_handler { |e|
|
|
63
|
+
logger.error "Exception in request: #{e}"
|
|
64
|
+
Vertx.set_timer(1_000) do
|
|
65
|
+
make_request
|
|
66
|
+
end
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
request.end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def body_handler(body)
|
|
73
|
+
if body.length > 0
|
|
74
|
+
handle_success(body)
|
|
75
|
+
else
|
|
76
|
+
handle_failure(:unknown)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def handle_success(raw_body)
|
|
82
|
+
body = JSON.parse(raw_body.to_s)
|
|
83
|
+
links = body['links']
|
|
84
|
+
|
|
85
|
+
body['entries'].reverse.map{|e|
|
|
86
|
+
logger.trace "Executing handler for #{e} with #{handler.inspect}"
|
|
87
|
+
::Retry.!(->(attempt){
|
|
88
|
+
handler.!(e, attempt)
|
|
89
|
+
#persist_successfully_handled_event(e['id'])
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if previous_link = links.find{|link| link['relation'] == 'previous'}
|
|
94
|
+
@request_string = previous_link['uri']
|
|
95
|
+
end
|
|
96
|
+
make_request
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def handle_failure(status_code)
|
|
100
|
+
logger.error "There was an error (#{status_code}) with the subscription request. Retrying"
|
|
101
|
+
Vertx.set_timer(rand(1000)+10) do
|
|
102
|
+
make_request
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
data/lib/retry.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class Retry
|
|
2
|
+
def self.!(block, attempt=0)
|
|
3
|
+
logger = Logger.get self
|
|
4
|
+
logger.debug "Executing #{block}, attempt: #{attempt}"
|
|
5
|
+
begin
|
|
6
|
+
block.call(attempt)
|
|
7
|
+
rescue RetryableError => e
|
|
8
|
+
logger.info "Exception in #{block}, retrying"
|
|
9
|
+
attempt += 1
|
|
10
|
+
Retry.!(block, attempt)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class RetryableError < StandardError ; end
|
metadata
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: event_store_http_client
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- j@obsidianexchange.com
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-04-07 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description:
|
|
14
|
+
email:
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- lib/event_store_http_client.rb
|
|
20
|
+
- lib/retry.rb
|
|
21
|
+
- lib/event_store_http_client/handler.rb
|
|
22
|
+
- lib/event_store_http_client/settings.rb
|
|
23
|
+
- lib/event_store_http_client/writer.rb
|
|
24
|
+
- lib/event_store_http_client/client/builder.rb
|
|
25
|
+
- lib/event_store_http_client/events/read.rb
|
|
26
|
+
- lib/event_store_http_client/events/write.rb
|
|
27
|
+
- lib/event_store_http_client/projection/get_state.rb
|
|
28
|
+
- lib/event_store_http_client/subscriptions/subscribe.rb
|
|
29
|
+
homepage:
|
|
30
|
+
licenses: []
|
|
31
|
+
metadata: {}
|
|
32
|
+
post_install_message:
|
|
33
|
+
rdoc_options: []
|
|
34
|
+
require_paths:
|
|
35
|
+
- lib
|
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 1.9.2
|
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
requirements: []
|
|
47
|
+
rubyforge_project:
|
|
48
|
+
rubygems_version: 2.1.9
|
|
49
|
+
signing_key:
|
|
50
|
+
specification_version: 4
|
|
51
|
+
summary: HTTP EventStore client
|
|
52
|
+
test_files: []
|